Thursday, June 14, 2007

Loading Resources in Sakai Components

I meant to blog about this a while ago but it has recently come up again and so I though I should write my ideas down. Ok so the problem is that you have a Sakai component which is created by the component manager and it wants to load something from inside the component, in my case it was an iBatis configuration file specified through a spring configuration file. So in my spring config I have something like:
  
<property name="configLocation">
 classpath:/sql-maps-config.xml
</property>
Now the problem is that this property is loaded through a PropertyEditor which sets the property to a ClassPathResource. In Spring the classloading policy is that resources should be loaded through the thread's classloader. When the component manager is starting up and so when my component is create this is caused by the fact that a web application has asked for it so the thread classloader points to the webapp and the resource in your component won't be found.

The solution to this is to have a way to load resources using the standard JVM Class.getResourceAsStream() which will use the classloader used to load the current class. So I created an class:

package org.sakaiproject.util;

import org.springframework.core.io.ClassPathResource;

/**
* Resource implementation for class path resources.
* Loads out of the same classpath as this class was loaded through.
*
* Supports resolution as java.io.File if the class path
* resource resides in the file system, but not for resources in a JAR.
* Always supports resolution as URL.
*
* @author buckett
*/
public class LocalClassPathResource extends ClassPathResource {

 public LocalClassPathResource(String path) {
  super(path, LocalClassPathResource.class.getClassLoader());
 }
}

and put this in my component. I also then change the configuration in my components.xml to use the resource directly:

<property name="configLocation">
<bean class="org.sakaiproject.util.LocalClassPathResource">
 <constructor-arg type="java.lang.String">
  <value>/sql-maps-config.xml</value>
 </constructor-arg>
</bean>
</property>
Now the configuration file gets loaded without any problems. Hopefully future Sakai developments in the component manager will mean this hack isn't needed later on.

No comments: