Integrating FeedBurner

Posted by Peter Donald Sat, 04 Mar 2006 00:33:00 GMT

I have been using Google Analytics to track my site traffic but up until now I have been not tracking who is accessing this site from my Atom and RSS Feeds. So I have started to publish my feeds through FeedBurner.

However I want the transition to be as smooth as possible and not interfer with users who had already subscribed to my feed. Initially I decided to redirect all of my feeds to FeedBurner by adding the following to my .htaccess file. This redirected all requests except those with a user agent of FeedBurner to the appropriate FeedBurner web site.

# Redirect typo feeds to FeedBurner
RewriteCond %{HTTP_USER_AGENT} !^FeedBurner$
RewriteRule ^xml/(atom|rss|rss20)/feed.xml$
       http://feeds.feedburner.com/RealityForge [R,L]

However this did not feel right as readers were being redirected to another web site. So I decided to reverse proxy the content from FeedBurner on my site. What this means is that anyone who accesses these urls is actually accessing content from the FeedBurner web servers but it appears as if it comes from my web site. My web server actually forwards the request to the feedburner website, gets their response and then returns it to the client.

# Redirect typo feeds to FeedBurner
RewriteCond %{HTTP_USER_AGENT} !^FeedBurner$
RewriteRule ^xml/(atom|rss|rss20)/feed.xml$
       http://feeds.feedburner.com/RealityForge [P,L]

Of course things aren’t always as easy as this. Unfortunately my web server is behind a firewall and another proxy server. So I needed to add the following config to my main apache config file.

ProxyRequests On
ProxyVia On

ProxyRemote http://feeds.feedburner.com/RealityForge
      http://proxy.cs.latrobe.edu.au:8080

This forwards any requests for http://feeds.feedburner.com/RealityForge to my local proxy server to handle.

And it all seems to work now!

Tags , , ,  | no comments | no trackbacks

Upload a file via POST with Net::HTTP

Posted by Peter Donald Thu, 02 Mar 2006 02:57:00 GMT

To upload a file to a website I needed to supply the data with a content type of “multipart/form-data”. The Net:HTTP API does not offer any such functionality, it just accepts raw content data. So I needed to roll my own.

The “multipart/form-data” content type consists of a number of secions separated by --BOUNDARY\r\n and terminated by BOUNDARY--\r\n where BOUNDARY is a string that does not appear in the content of any of the data transmitted to the server.

Each section represents a form field and contains a number of headers, a \r\n, the content and finishes with a \r\n. Normal form fields look like

Content-Disposition: form-data; name="mykey"

mydata

while file fields must include a few more headers.

Content-Disposition: form-data; name="mykey"; filename="filename"
Content-Transfer-Encoding: binary
Content-Type: text/plain

DATADATADATADATADATADATADATA...

To construct the parameters in ruby I use the following code;

def text_to_multipart(key,value)
  return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"\r\n" + 
         "\r\n" + 
         "#{value}\r\n"
end

def file_to_multipart(key,filename,mime_type,content)
  return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"; filename=\"#{filename}\"\r\n" +
         "Content-Transfer-Encoding: binary\r\n" +
         "Content-Type: #{mime_type}\r\n" + 
         "\r\n" + 
         "#{content}\r\n"
end

To put it all together you need to join the parameters with boundary separators between each section. This can be done via

boundary = '349832898984244898448024464570528145'
query = 
  params.collect {|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"

The last thing that needs to be done is to make sure that you set the HTTP Header Content-type to multipart/form-data; boundary=BOUNDARY.

A complete example that I extracted from code that uploads a css file to the w3c validator service is as follows.

params = [ 
  file_to_multipart('file','file.css','text/css',data),
  text_to_multipart('warning','1'),
  text_to_multipart('profile','css2'),
  text_to_multipart('usermedium','all') ]

boundary = '349832898984244898448024464570528145'
query = 
  params.collect {|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"

response = http.start('jigsaw.w3.org').
  post2("/css-validator/validator",
        query,
        "Content-type" => "multipart/form-data; boundary=" + boundary)

It was a little bit painful to figure out “multipart/form-data” via Ethereal but relatively easy to implement. Hope this helps!

Update 3rd of October, 2006:

Slight correction supplied by Andrew Willis so that last boundary is '--' + boundary + '--' rather than just boundary + '--'.

Posted in  | Tags ,  | no comments | no trackbacks

Removing Stale Rails Sessions

Posted by Peter Donald Wed, 01 Mar 2006 00:53:00 GMT

By default rails does not clear out stale sessions from the session store. To implement this feature I added the following small snippet of code;

class SessionCleaner
  def self.remove_stale_sessions
    CGI::Session::ActiveRecordStore::Session.
      destroy_all( ['updated_on <?', 20.minutes.ago] ) 
  end
end

And then invoke the remove_stale_sessions method every 10 minutes via;

*/10 * * * * ruby /full/path/to/script/runner 
   -e production "SessionCleaner.remove_stale_sessions"

Posted in  | Tags ,  | no comments | no trackbacks

Flexible Application Configuration in Rails

Posted by Peter Donald Tue, 28 Feb 2006 10:37:00 GMT

Most of my rails application have required some form of application specific configuration. When the configuration varies depending on where I deploy it I prefer to externalize the configuration in a YAML file similar to the way database configuration is handled.

I remember reading a blog somewhere that suggested the use of OpenStruct and have since started using the following code placed at the bottom of my config/environment.rb file.

require 'ostruct'
require 'yaml'

ConfigFile = "#{RAILS_ROOT}/config/config.yml"
if File.exist?(ConfigFile)
 ::ApplicationConfig = OpenStruct.new(YAML.load_file(ConfigFile))
end

Consider the scenario where config/config.yml contains;

proxy_config:
  host: proxy.cs.latrobe.edu.au
  port: 8080

I could then access the the configuration data using

>> ApplicationConfig.proxy_config['host'] 
=> "proxy.cs.latrobe.edu.au"
>> ApplicationConfig.proxy_config['port']
=> 8080

To make your code more robust in the scenario where either the proxy configuration is missing or the whole configuration file is missing I generally add in extra guards. Below is an example of the code I use to instantiate a Net:HTTP object. If the configuration file is present and it contains a proxy_config section then the code will attempt to instantiate a Net::HTTP::Proxy object. Otherwise the code will instantiate a plain old Net::HTTP that will not attempt to go via a proxy server.

def http
  if Module.constants.include?("ApplicationConfig") && 
      ApplicationConfig.respond_to?(:proxy_config)
    host = ApplicationConfig.proxy_config['host']
    port = ApplicationConfig.proxy_config['port']
    Net::HTTP::Proxy(host, port)
  else
    Net::HTTP
  end
end

Posted in  | Tags , ,  | no comments | no trackbacks

PanMX: Exposing MBeans the easy way

Posted by Peter Donald Mon, 27 Feb 2006 05:15:00 GMT

PanMX removes the pain from developing MBeans. It was a toolkit I developed years ago to make things less painful. I have been cleaning up my subversion repositories and I figured it may be useful for others. So here it is released to the public at large.

The user can use Java 1.5 Annotations to define the MBean interface or allow the toolkit to derive the MBean interface using reflection.

The toolkit allows the user to define ModelMBeans via the ModelMBeanFactory or RMXBeans which are very similar MXBeans via the RMXBeanFactory .

Annotating a Class

An example of how to annotate a Java Class.

@MBean(description = "Component to measure and clean out toxins")
  public class Liver
{
  private float m_toxicity;

  @MxAttribute()
    public float getToxicity()
  {
    return m_toxicity;
  }

  @MxAttribute(description = "Level of toxicity")
    public void setToxicity(final float toxicity)
  {
    m_toxicity = toxicity;
  }

  @MxOperation(description = "Clean out the toxins")
    public void clean()
  {
    m_toxicity = 0;
  }
}

Exposing an MBean on the ‘Server’

To expose an instance of the Liver class as a ModelMBean the user could add the following code;

final Liver liver = new Liver();
final ObjectName liver = new ObjectName("body:part=Liver");
final ModelMBean mBean =
  ModelMBeanFactory.createAnnotatedModelMBean( liver );
final MBeanServer mBeanServer =
  ManagementFactory.getPlatformMBeanServer();
mBeanServer.registerMBean( mBean, objectName );

To expose the object as an RMXBean the user could use the following code;

final Liver liver = new Liver();
final ObjectName liver = new ObjectName("body:part=Liver");
final Object mBean =
  RMXBeanFactory.createAnnotatedRMXBean( liver );
final MBeanServer mBeanServer =
  ManagementFactory.getPlatformMBeanServer();
mBeanServer.registerMBean( mBean, objectName );

Accessing an MBean on the ‘Client’

The user can access the MBeans using the standard mechanisms. Additionally if the user decides to use RMXBeans and has defined the management interface using a java interface then the object can be dynamically proxied. This means that the user can interact with the object as if it was a local object.

The following code example demonstrates accessing a RMXBean as a dynamic proxy.

public interface LiverRMXBean
{
  @MxAttribute()
  float getToxicity();

  @MxAttribute(description = "Level of toxicity")
  void setToxicity(float toxicity);

  @MxOperation(description = "Clean out the toxins")
  void clean();
}

@MBean(description = "Component to measure and clean out toxins", 
       interfaces = {LiverMBean.class})
  public class Liver
  implements LiverMBean
{
  private float m_toxicity;

  public float getToxicity()
  {
    return m_toxicity;
  }

  public void setToxicity(final float toxicity)
  {
    m_toxicity = toxicity;
  }

  public void clean()
  {
    m_toxicity = 0;
  }
}

...
// Registering the Liver MBean
final Liver liver = new Liver();
final ObjectName liver = new ObjectName("body:part=Liver");
final Object mBean =
  RMXBeanFactory.createAnnotatedRMXBean( liver );
final MBeanServer mBeanServer =
  ManagementFactory.getPlatformMBeanServer();
mBeanServer.registerMBean( mBean, objectName );
...

...
// Accessing and using the Liver MBean
final MBeanServerConnection connection = ...;
final ObjectName liver = new ObjectName("body:part=Liver");
final LiverRMXBean liver = (LiverRMXBean)
  RMXBeanFactory.newProxyInstance( liver );

System.out.println( "Current Toxicity Level: " + liver.getToxicity() );
liver.clean();
System.out.println( "Toxicity Level after cleaning: " + liver.getToxicity() )
...

Files

To get your greasy mits on PanMX grab it from one of the following locations

Posted in  | Tags , , ,  | no comments | no trackbacks

Older posts: 1 2 3 4 5 6