PDA

View Full Version : CLASSPATH_ALL_URL_PREFIX, component-scan in OSGi environment


dlkpochtaws
May 14th, 2008, 01:47 AM
We are using Spring v2.5+ Spring Dynamic Modules in OSGi container, Felix in our case.
Also we are planning to use Spring’s @Component scanning.

<context:component-scan base-package="org.package" />


But unfortunately it appears Spring DM does not support component-scanning. Because it assumes
ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX= "classpath*:"
can not be resolved in OSGi environment. I agree it can not be resolved for all possible cases.

But in our situation, we assume, components must be scanned only in current bundle.
So, we’ve created a small aspect:

/**
* Aspect help class scaner from spring-context to receive classes from bundle with
* bundle specific PatternResolver
* @author Ivan Martyushev
* 25.03.2008
*/
@Aspect
public class ClasspathPrefixForOsgiAspect {
private Logger logger = LoggerFactory.getLogger(ClasspathPrefixForOsgiAspe ct.class);

@Pointcut("execution(* " +
"org.springframework.osgi.context.support.AbstractO sgiBundleApplicationContext.getResources(String)) " +
"&& args(locationPattern)")
void execution_getResources(String locationPattern) {
}

@Around("execution_getResources(locationPattern)")
public Resource[] around_getResources(ProceedingJoinPoint methodExecution, String locationPattern) throws Throwable {
logger.debug("Current locationPattern = {}", locationPattern);
return (Resource[]) methodExecution.proceed(new Object[] {fixLocationPatternIfNeeded(locationPattern)});
}

String fixLocationPatternIfNeeded(String locationPattern) {
if ((locationPattern.startsWith(ResourcePatternResolv er.CLASSPATH_ALL_URL_PREFIX))) {
String result = OsgiBundleResource.BUNDLE_URL_PREFIX +
locationPattern.substring(ResourcePatternResolver. CLASSPATH_ALL_URL_PREFIX.length());
logger.debug("FixedLocationPattern = {}", result);
return result;
}
return locationPattern;
}
}


As we can see, this aspect catches calling to AbstractOsgiBundleApplicationContext.getResources( String) and converts resorcepaths with CLASSPATH_ALL_URL_PREFIX to resourcepaths from current bundle (BUNDLE_URL_PREFIX).

Of course, this solution is not suitable for all, but it is enough for some cases. It would be nice to have similar approach in Spring DM out of the box. In any case, by default it just generates an exception - not a user-friendly approach:-)

Costin Leau
May 14th, 2008, 02:04 AM
It depends on the version of Spring-DM you are using Dmitry. Spring-DM 1.0.x does not support classpath*: but Spring-DM 1.1.x does.
Additionally, note that classpath* is not equivalent to a search inside BUNDLE_URL_PREFIX.
The former literally searches all the bundle classpaths that are wired to the current bundle while the former only searches the bundle space (which means the bundle class loader doesn't even have to be resolved to work). In general, the bundle space = the bundle jar + attached fragments which is different from the bundle class path.

Give Spring-DM 1.1 M2 a try and let me know how that works.

dlkpochtaws
May 14th, 2008, 02:09 AM
Thank you for quick answer.
We are using 1.0.2. May be I'll try 1.1.x later.


The former literally searches all the bundle classpaths ... while the former only searches ....
Sorry, I don't see, which is former, and which is latter :-)

Costin Leau
May 14th, 2008, 08:10 AM
Sorry about that.
Former = classpath*:
Latter = osgibundle:

Note that there is also a subtle difference between classpath*: and classpath:

Cheers,

volvin
May 19th, 2008, 03:26 PM
Hi,

I tried to component scan without success on Spring DM version 1.1.0 M2 with Spring 2.5.4.

The following context.xml is in the META-INF/spring directory of Bundle A:


...

<context:annotation-config/>
<context:component-scan annotation-config="true" base-package="my.testbundle"/>

<osgi:reference id="service1" interface="test.service"/>

...


I've created a component in the same bundle:


package my.testbundle;

import test.Service;

@Component
public class Launcher {
private Service service = null;

public Launcher(){
}

@Autowired
public void setService(Service service){
this.service = service;
}
}


I get no errors but the Launcher is not created.
Am I missing something?
Thx

volvin
May 19th, 2008, 07:55 PM
Hi,

I got it working after packaging and installing the bundle jar via the osgi console. Nice work!

I didn't got it working when launching it directly in eclipse. Has this something to do with the exploded form in which the bundle is deployed in eclipse?

Thx

Costin Leau
May 20th, 2008, 04:34 AM
It might be related - I haven't tested the expanded version though I would assume it should work since we're using the OSGi API and not the file system so as long as Equinox/Eclipse do the translation things should be working.
Can you please try enabling logging (trace level) at least on the IO side to see what is going on?
Additionally you could try the RC1 nightly build (see the home page for nightly builds) and see whether that makes a difference. The getFile() mechanism has been improved though that shouldn't affect the classpath discovery.

sheak
Jul 24th, 2008, 07:24 PM
After reading this thread I got to debugging into the OsgiBundleResourcePatternResolver and found that when I specify <context:component-scan base-package="*"/> the component scanning works within the eclipse pde/rcp environment! The resulting Bundle.findEntries call is: bundle.findEntries("/", null, false);. I'm assuming it is something to with the way pde creates a bundle from the project. I thought that this was controlled by the build.properties file but haven't had any luck with any settings I choose. Does anyone have any ideas?

Costin Leau
Jul 25th, 2008, 03:15 AM
Maybe I don't understand this properly but if it's working, why is this unexpected?
Assuming that it doesn't, do you know how your bundle is stored and saved? Even for expanded jars, Equinox should still properly expose the bundle through the OSGi API and should obey the contract of the calls made to it.

sheak
Jul 27th, 2008, 07:14 PM
Thanks for your reply Costin,

This is definately a specific to eclipse PDE so this may not be the best forum for the problem but this is what I found. When I use <context:component-scan base-package="*"/> the following bundle entries are found Resolved location pattern [*/**/*.class] to resources [ [bundleentry;||4|target|classes|mypackage|EntryList er.class] Note the target/classes in the bundle entry url. So the target/classes directory is part of the bundle. When I change to <context:component-scan base-package="mypackage"/> nothing is found because pattern doesn't match. I guess I need a way to instruct PDE to include the output directory target/classes in the root of the bundle. Anyone have a solution to this?

Note: i have used '|' instead of '/' in the bundle entry url because of the forum restrictions.

Costin Leau
Jul 28th, 2008, 03:04 AM
It might be indeed something PDE specific. It seems that the target/classes is considered as part of the classpath but it's not enlisted under Bundle-ClassPath: manifest. That's why when you do scanning, the first one works (since you're scanning all resources) while the latter fails (mypackage is not available under the default classpath (.) and since target/classes is not a classpath, it's not considered).
One way to make this work is to add target/classes to the Bundle-ClassPath:

Bundle-ClassPath: .,target/classes

Missing entries will be ignored but existing ones will be used for scanning. Give it a try and let me know how it goes.

sheak
Jul 30th, 2008, 09:10 PM
Yes that worked. Thought I'm not sure that including the target/classes in the Bundle-ClassPath is the best approach as it is only valid in the eclipse PDE runtime environment.

Costin Leau
Jul 31st, 2008, 01:30 AM
Indeed but it shouldn't hurt in other environments since if target/classes/ is missing or if it contains no entries, it will be simply ignored. Ideally PDE would not create this entries but since it does, adding it to the classpath provides a simple workaround w/o many (if any) side-effects.