DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. OSGi: jumping through classoading hoops

OSGi: jumping through classoading hoops

JS Bournival user avatar by
JS Bournival
·
May. 28, 10 · Interview
Like (0)
Save
Tweet
Share
5.81K Views

Join the DZone community and get the full member experience.

Join For Free

To set the context of this post, let's have a look at the original use case leading to the problem that caused the solution.

The use case

I wanted to use the Rome library to consume/produce RSS/Atom feeds. I have used it before on traditional JEE projects, but never in an OSGi container.

The environement

I am using Fuse ESB 4.1, Progress' commercial distribution of Apache ServiceMix 4 OSGi runtime, based on Apache Felix. The JAX-RS implementation used is Apache CXF v2.2. All of this running on my macbook pro, under Java 1.6.

I deployed Rome 1.0 jar in Fuse ESB 4. This jar contains the necessary metadata in it's manifest to be deployed in an OSGi container.

The code

To simplify things, let's assume a JAX-RS resource class publishing a feed:

SyndFeed feed = new SyndFeedImpl();
feed.setFeedType("rss_2.0");

feed.setTitle("Sample Feed (created with ROME)");
feed.setLink("http://rome.dev.java.net");
feed.setDescription("This feed has been created using ROME (Java syndication utilities");

// ... add entries

StringWriter sw = new StringWriter();
SyndFeedOutput output = new SyndFeedOutput();
output.output(feed, sw);

return Response.ok(sw.toString()).build();

This code is deployed as an OSGi bundle, depending on CXF, JAX-RS spec, and Rome (among other things).

The problem

When I run this code and call the URL for this resource, I get a strange exception, referring to the SyndFeedImpl class not initializing correctly. What? I only called the default constructor, what's wrong with that??

org.apache.cxf.interceptor.Fault: Could not initialize class com.sun.syndication.feed.synd.SyndFeedImpl
at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:148)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:114)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:122)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:70)

... [snip]

Digging through Rome source code, I found that all the plugins (I/O components + various helper implementations) are specified in a properties file somewhere on the classpath (some poor man's DI). Rome reads this file so it can instantiate these classes. But to do so, it needs to ask a classloader to load them first. Let's have a look at how this is implemented:

PropertiesLoader loader = (PropertiesLoader)
clMap.get(Thread.currentThread().getContextClassLoader());
if (loader == null) {
try {
loader = new PropertiesLoader(MASTER_PLUGIN_FILE, EXTRA_PLUGIN_FILE);
clMap.put(Thread.currentThread().getContextClassLoader(), loader);
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
return loader;

So here, we can clearly see that while reading the plugins configuration, it stores these properties alongside the classloader to be used to load the classes. The classloader used is Thread.currentThread().getContextClassLoader().

BAM!

At runtime, when inspecting what this classloader resolves to, we find that it is set to:

System.out.println(Thread.currentThread().getContextClassLoader().toString());
$ [Apache ServiceMix CXF Transport for OSGi (org.apache.servicemix.cxf.transport.osgi)]

Which is the classloader for an entirely different bundle. However, to achieve true modularity, OSGi's architecture tells us (section 3.4 second par.) that for each bundle there is one classloader. So Rome cannot load and instantiate its plugins (dynamically in this case).

Solution 1

Procrastinate, and by doing so, bill your client even more.

Solution 2

Open up Rome's maven project in Netbeans, and fix the code. But isn't classloading stuff a complicated matter? it depends. One easy way to fix it, is to figure out how OSGi is working. When it is installing a bundle, even before resolving this bundle's dependencies, the framework creates a classloader, a private & cozy place where the bundle's classes will not get disturbed/shadowed by others (read: jar hell). This classloader is the only one who can load classes from this bundle, since it's the only one seeing them (not entirely true, but anyways).

So, it's easy to ask a class for it's classloader, by simply doing something like:

ClassLoader cl = this.getClass().getClassLoader();

So now, If I change the code to use this classloader, and I inspect what it resolves to, I get:

System.out.println(this.getClass().getClassLoader().toString());
$ [211.0]

Which represents Rome bundle ID inside of Fuse ESB 4.

Conclusion

I opted for solution 2 ;) Which solution forced me to recompile a special Rome version and store it in our internal Maven repo.

I learned that sometimes, there's more to osgifiying a jar than just providing a nice manifest. Enterprise systems developers must be aware of that sort of stuff before just adding a jar to a project.

Maybe other solutions would have been viable: Equinox buddy visibility manifest headers? Dynamic-imports? (althought I read somewhere that this must be used as a last resort solution as it breaks OSGi modularity principles).

I'd be happy to hear other point of views.

JS.
Fuse ESB

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Data Stream Using Apache Kafka and Camel Application
  • Scaling Your Testing Efforts With Cloud-Based Testing Tools
  • Best Practices for Setting up Monitoring Operations for Your AI Team
  • Demystifying Multi-Cloud Integration

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: