Sunday, June 7, 2009

Joomla! Displaying Links to Blogger posts.

My website, http://www.kimenye.com, is powered by Joomla! (one of the world's most popular open source content management system). Not being a fun of its usability as a blogging platform, I made the decision to host this blog in Google's Blogger service. Having done that, I found I needed to display links to my blog from my website in a dynamic manner, that didn't require me to manually add links when I add new content. As part of my website, I have a panel where I intend to display the Titles and Links to the blog posts. This article describes how I achieved this.

Pre-Requisites
  1. Apache Web Server (preferably 2.0)
  2. PHP 5
  3. Zend GData PHP Library

Configuration of Zend PHP Library
We will be connecting to the Google Data Services to retrieve posts for the blog. This is done using the Zend GData PHP Library.  See this article on the Google Code pages with detailed information on how to configure the libary. There's also a video on the page by one of Google's Software Engineers with step by step instructions. If you are on shared hosting, your mileage may vary following those instructions. I'll outline the failed instructions briefly.
  1. Upload the Zend Framework library. From the unpacked archive, we need everything in the folder library (i.e the folder XXX\library\Zend).
  2. Edit the .htaccess file to include the Zend Library folder in the include path. 
This did not work for me, as my hosting provider has moved to using PHPSUEXEC and hence don't allow any php directives in the .htaccess file. These have to be moved to a php.ini file created in your public_html folder. I did this, however, this php.ini file was not getting parsed and hence my includes were failing. I checked my configuration using the phpinfo() method and saw that new php.ini file was not being parsed and hence the include_path variable was not being updated.

Using the Zend GData Library

  1. <?php
  2. // no direct access
  3. defined('_JEXEC') or die('Restricted access');
  4. //This is the path of where you have uploaded your Zend GData library
  5. $path = '/home/your_user_name/public_html/libraries/';
  6. //This method updates the include path to include the Zend GData libaries
  7. //This is useful if you are in a shared hosting environment where it is not
  8. //easy to update the include path
  9. set_include_path(get_include_path() . PATH_SEPARATOR . $path);
  10. //require the Zend Loader file
  11. require_once (dirname(__FILE__).DS.'Zend'.DS.'Loader.php');
  12. /**
  13. * @see Zend_Gdata
  14. */
  15. Zend_Loader::loadClass('Zend_Gdata');
  16. /**
  17. * @see Zend_Gdata_Query
  18. */
  19. Zend_Loader::loadClass('Zend_Gdata_Query');
  20. /**
  21. * @see Zend_Gdata_ClientLogin
  22. */
  23. Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
  24. /**
  25. * This is the class to use to connect to the Google Blogger Service
  26. */
  27. class BloggerHelper
  28. {
  29. function getList()
  30. {
  31. $count = 5; //this is the number of items to be returned
  32. $blogId = 123456789; //this is your blog id
  33. $email = 'someone@gmail.com'; //This is the email to log into Blogger
  34. $password = 'password'; //This is the password to log into Blogger
  35. $client = Zend_Gdata_ClientLogin::getHttpClient($email, $password, 'blogger');
  36. $gdClient = new Zend_Gdata($client);
  37. $query = new Zend_Gdata_Query('http://www.blogger.com/feeds/' . $blogId . '/posts/default');
  38. $query->setMaxResults($count);
  39. $feed = $gdClient->getFeed($query);
  40. $i = 0;
  41. $lists = array();
  42. foreach($feed->entries as $entry) //This is a temp hack to remove drafts
  43. {
  44. $lists[$i]->link = htmlspecialchars($entry->link[1]->text);
  45. $lists[$i]->text = $entry->title->text;
  46. $i++;
  47. }
  48. return $lists;
  49. }
  50. }

The code shown above requires you to know your blog id. This can be obtained by looking at the request parameters in the url for your blog. A simple way to find this is to log into your Blogger account, click on the blog you want to access and click on the settings link. Now look at the url on your blog. It should have something like "http://www.blogger.com/blog-options-basic.g?blogID=1234567890". The numbers after the string "blogID=" is your blog id. 

The getList() method returns an array of objects with the link as the url to the blog post and the text property as the title of the blog. This array of items is then used by a Joomla! module to display this information on the screen.

I've not found an elegant way to distinguish the drafts from the published posts, hence the current way of using the URL will have to do for now. In the future I intend to add some additional logic to do searching based on key words, so it works like an aggregator as well as functionality to display posts published in the last 30 days. Hope this is a nice starting place.

Saturday, June 6, 2009

Google App Engine Wicket ModificationWatcher

What is the ModificationWatcher?
ModificationWatcher is a Wicket class that checks to see if resources have been modified and reloads them without restarting the servlet container (where possible). This is made possible by setting up a polling frequency in the init() method of the WebApplication class for your project. Unfortunately, Google App Engine does not allow the spawning of new threads (on which the ModificationWatcher class depends on), hence a work-around is necessary. First we need to look at the WicketApplication class, as shown in the example below:

Example WicketApplication Class
view plaincopy to clipboardprint?
  1. import org.apache.wicket.Application;
  2. import org.apache.wicket.protocol.http.HttpSessionStore;
  3. import org.apache.wicket.protocol.http.WebApplication;
  4. import org.apache.wicket.protocol.http.WebRequest;
  5. import org.apache.wicket.session.ISessionStore;
  6. import org.apache.wicket.util.lang.PackageName;
  7. public class WicketApplication extends WebApplication {
  8. @Override
  9. protected void init() {
  10. super.init();
  11. // getResourceSettings().setResourcePollFrequency(null);
  12. }
  13. protected WebRequest newWebRequest(HttpServletRequest servletRequest) {
  14. getResourceSettings().getResourceWatcher(true).start(
  15. getResourceSettings().getResourcePollFrequency());
  16. return super.newWebRequest(servletRequest);
  17. }
  18. @Override
  19. public String getConfigurationType() {
  20. return Application.DEVELOPMENT;
  21. // return Application.DEPLOYMENT;
  22. }
  23. @Override
  24. protected ISessionStore newSessionStore() {
  25. return new HttpSessionStore(this);
  26. // return new SecondLevelCacheSessionStore(this, new DiskPageStore());
  27. }
  28. }

The first 'hack' to getting Wicket working in GAE is to set the session store type for Wicket to HttpSessionStore, instead of using the SecondLevelCacheSessionStore as the latter directly refers to the file system and is not allowed in the app engine sandbox. This is done by overriding the newSessionStore() method [line 29 in the code extract shown above].

Now, back to the ModificationWatcher. If you want to completely disable it, you can set the poll frequency to null in the init method of your WebApplication class [this has been commented out in line 13 above]. However, if you are in development, this will mean that any changes to the source pages [in my experience its really been the HTML pages], you will have to restart your servlet container to pick-up the changes, which is not very efficient for development purposes. This work-around by-passes the polled watching for file changes and checks to see if the file has changed every time it is requested for. This is achieved by overriding the newWebRequest() method [see line 16 above]. This is fine for development purposes, and MUST be disabled for production, for obvious reasons. 

And for this to work, you need a version of the ModificationWatcher class that does not spawn any threads. ModificationWatcher was declared a final class and hence cannot be extended, and so you need to override the Wicket provided version with one of your own. This is easily achieved by creating a similar package structure in your source code and creating the file : org.apache.wicket.util.watch.ModificationWatcher. The code for this class is as below:


  1. /**
  2. * Monitors one or more <code>IModifiable</code> objects, calling a
  3. * {@link IChangeListener IChangeListener} when a given object's modification time changes.
  4. *
  5. * @author Jonathan Locke
  6. * @since 1.2.6
  7. */
  8. public final class ModificationWatcher
  9. {
  10. /** logger */
  11. private static final Logger log = LoggerFactory.getLogger(ModificationWatcher.class);
  12. /** maps <code>IModifiable</code> objects to <code>Entry</code> objects */
  13. private final Map modifiableToEntry = new ConcurrentHashMap();
  14. /** the <code>Task</code> to run */
  15. //private Task task;
  16. /**
  17. * Container class for holding modifiable entries to watch.
  18. */
  19. private static final class Entry
  20. {
  21. // The most recent lastModificationTime polled on the object
  22. Time lastModifiedTime;
  23. // The set of listeners to call when the modifiable changes
  24. final ChangeListenerSet listeners = new ChangeListenerSet();
  25. // The modifiable thing
  26. IModifiable modifiable;
  27. }
  28. /**
  29. * Default constructor for two-phase construction.
  30. */
  31. public ModificationWatcher()
  32. {
  33. }
  34. /**
  35. * Constructor that accepts a <code>Duration</code> argument representing the poll frequency.
  36. *
  37. * @param pollFrequency
  38. * how often to check on <code>IModifiable</code>s
  39. */
  40. public ModificationWatcher(final Duration pollFrequency)
  41. {
  42. start(pollFrequency);
  43. }
  44. /**
  45. * Adds an <code>IModifiable</code> object and an <code>IChangeListener</code> object to
  46. * call when the modifiable object is modified.
  47. *
  48. * @param modifiable
  49. * an <code>IModifiable</code> object to monitor
  50. * @param listener
  51. * an <code>IChangeListener</code> to call if the <code>IModifiable</code> object
  52. * is modified
  53. * @return <code>true</code> if the set did not already contain the specified element
  54. */
  55. public final boolean add(final IModifiable modifiable, final IChangeListener listener)
  56. {
  57. // Look up entry for modifiable
  58. final Entry entry = (Entry)modifiableToEntry.get(modifiable);
  59. // Found it?
  60. if (entry == null)
  61. {
  62. if (modifiable.lastModifiedTime() != null)
  63. {
  64. // Construct new entry
  65. final Entry newEntry = new Entry();
  66. newEntry.modifiable = modifiable;
  67. newEntry.lastModifiedTime = modifiable.lastModifiedTime();
  68. newEntry.listeners.add(listener);
  69. // Put in map
  70. modifiableToEntry.put(modifiable, newEntry);
  71. }
  72. else
  73. {
  74. // The IModifiable is not returning a valid lastModifiedTime
  75. log.info("Cannot track modifications to resource " + modifiable);
  76. }
  77. return true;
  78. }
  79. else
  80. {
  81. // Add listener to existing entry
  82. return entry.listeners.add(listener);
  83. }
  84. }
  85. /**
  86. * Removes all entries associated with an <code>IModifiable</code> object.
  87. *
  88. * @param modifiable
  89. * an <code>IModifiable</code> object
  90. * @return the <code>IModifiable</code> object that was removed, else <code>null</code>
  91. */
  92. public IModifiable remove(final IModifiable modifiable)
  93. {
  94. final Entry entry = (Entry)modifiableToEntry.remove(modifiable);
  95. if (entry != null)
  96. {
  97. return entry.modifiable;
  98. }
  99. return null;
  100. }
  101. /**
  102. * Starts watching at a given <code>Duration</code> polling rate.
  103. *
  104. * @param pollFrequency
  105. * the polling rate <code>Duration</code>
  106. */
  107. public void start(final Duration pollFrequency)
  108. {
  109. // Construct task with the given polling frequency
  110. //task = new Task("ModificationWatcher");
  111. //task.run(pollFrequency, new ICode()
  112. //{
  113. // public void run(final Logger log)
  114. // {
  115. // Iterate over a copy of the list of entries to avoid
  116. // concurrent
  117. // modification problems without the associated liveness issues
  118. // of holding a lock while potentially polling file times!
  119. for (final Iterator iterator = new ArrayList(modifiableToEntry.values()).iterator(); iterator
  120. .hasNext();)
  121. {
  122. // Get next entry
  123. final Entry entry = (Entry)iterator.next();
  124. // If the modifiable has been modified after the last known
  125. // modification time
  126. final Time modifiableLastModified = entry.modifiable.lastModifiedTime();
  127. if (modifiableLastModified.after(entry.lastModifiedTime))
  128. {
  129. // Notify all listeners that the modifiable was modified
  130. entry.listeners.notifyListeners();
  131. // Update timestamp
  132. entry.lastModifiedTime = modifiableLastModified;
  133. }
  134. }
  135. // }
  136. //});
  137. }
  138. /**
  139. * Stops this <code>ModificationWatcher</code>.
  140. */
  141. public void destroy()
  142. {
  143. //if (task != null)
  144. //{
  145. // task.stop();
  146. // task.interrupt();
  147. //}
  148. }
  149. /**
  150. * Retrieves a key set of all <code>IModifiable</code> objects currently being monitored.
  151. *
  152. * @return a <code>Set</code> of all <code>IModifiable</code> entries currently maintained
  153. */
  154. public final Set getEntries()
  155. {
  156. return modifiableToEntry.keySet();
  157. }
  158. }

All that has been done is that the Threading references have been taken out so no new threads are spawned and the check for the resources is done in the same thread as that the one that is servicing the user request. This is not the most elegant solution, but it works.

This hack is based on the following sources: