- Apache Sling - http://sling.apache.org/ (graduated)
http://incubator.apache.org/sling/site/index.html - Apache Felix - http://felix.apache.org/
- Sling SVN (trunk) - http://svn.apache.org/repos/asf/sling/trunk/ (graduated)
http://svn.eu.apache.org/repos/asf/incubator/sling/trunk
http://www.theserverside.com/tt/articles/article.tss?l=WhiteboardForOSGi
http://www.knopflerfish.org/osgi_service_tutorial.html
Here are the steps I took to get Sling up and running (more details on the Sling site):
- Checkout the source code using subversion:
svn co http://svn.apache.org/repos/asf/sling/trunk sling
(I checked out revision 758703 since the current revision was not working) - Build the source using maven 2:
cd sling
mvn clean install - Run Sling using the built in Jetty webserver (as an executable jar):
java -jar launchpad/app/target/org.apache.sling.launchpad.app-5-SNAPSHOT.jar
(Note: the version of the jar will change over time)
(you can also run drop a sling war into your own servlet container)
(you can change the port by adding "-p #" (where # is the port number like 9090))
(you can cause all logs to go to the console by adding "-f -")
(you can change the logging level of sling by adding "-l #" (where # is 0-4 with 4=debug)) - Test out sling using your web browser:
Go to http://localhost:8080/
Click on the console link (login as admin/admin)
- Run Sling with debugging options enabled:
java -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=9001,server=y,suspend=n -jar launchpad/app/target/org.apache.sling.launchpad.app-5-SNAPSHOT.jar
(You should see this in the logs: Listening for transport dt_socket at address: 9001) - Attach the debugger from eclipse (or use whatever debugger you like):
Run -> Debug Configurations
Right click Remote Java Application -> New
Connect tab: Set Port to 9001
Source tab: Add the imported Sling code and your OSGi bundle project
Click the Debug button at the bottom
(You should not get an error if everything connects up) - Place a breakpoint in Runtime class on the gc method
- Click on System Information and then the Run button next to Garbage Collection
(the debugger should pick up the call and pause the JVM at the breakpoint) - Use the debugger to trace through if you like (but bear in mind that the admin console is not actually part of Sling, it is part of Apache Felix so you will need the source for Felix)
- http://felix.apache.org/site/
apache-felix-maven-bundle- plugin-bnd.html - http://felix.apache.org/site/
apache-felix-bundle-plugin- faq.html - http://aqute.biz/Code/Bnd
<dependencies>
<!-- OSGi -->
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi_R4_core</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi_R4_compendium</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Activator>org.azeckoski.osgi.SampleActivator</Bundle-Activator>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
- Checkout the sample bundle code:
svn co https://source.sakaiproject.org/contrib/caret/osgi-sample/tags/sample-1.1/ osgi-sample - Build the bundle using maven 2:
cd osgi-sample
mvn clean install - Access the Felix console for bundles (included in Sling):
http://localhost:8080/system/console/bundles - Browse and select the bundle (target/sample-1.1.jar)
- Click Install or Update and then Refresh Packages
- Scroll down to the Sample OSGi Bundle
If it is listed then you have a properly installed bundle! - Click the Start and then Stop buttons to the right of the bundle
- You should see something like this in the logs:
Sample starting at: Mon Apr 13 13:15:27 BST 2009
Sample stopping at: Mon Apr 13 13:15:29 BST 2009 - Now click Start to make sure the bundle is running
- Restart Sling (use the admin console or just kill it and rerun the command)
- You should see that the bundle is running (it was started automatically) and in the startup logs the Sample starting... should appear
import java.util.Date;Congratulations. You have installed you first bundle. If you want to try out the debugging you can put breakpoints in the start and stop methods. It only took me a couple days to get to this point which I think is not great so I hope this tutorial has really sped up the process for you.
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class SampleActivator implements BundleActivator {
public void start(BundleContext context) throws Exception {
System.out.println("Sample starting at: " + new Date());
}
public void stop(BundleContext context) throws Exception {
System.out.println("Sample stopping at: " + new Date());
}
}
Now for the advanced bit. Creating your own bundle. I have a somewhat realistic set of bundles where one depends on another and one uses the http.service (and other bundles) so chances are you won't face things that are too much more complex than this.
There are a few things that I learned going through this that were not all that apparent so hopefully I can save you some digging with these pro-tips:
- OSGi start order does not guarantee service start order - The order which bundles start does not guarantee that their services will also be started. In fact, relying on this is a mistake since OSGi is meant to be modular and services could go away at any time (see the note at the top about OSGi and whiteboard pattern). Code should be written to expect that a service might not be ready to use when the bundle starts. Waiting for services to be available is bad also because OSGi activators are expected to be quick so delaying them will cause failures.
- Bundles should import the packages they export - This is not obvious but it makes sense when you think about it. Since there may be other bundles of higher rank that are exporting packages that match yours, you should use the highest priority, even in your own bundle. This behavior is actually taken care of my most bundle making tools automatically so just don't be surprised to see your packages in the imports.
- javax packages will need to be imported - Only java.* is available to your bundle by default so all the dom,xml,net,wtc. stuff from javax has to imported. If you do not import it and happen to use it then your bundle will fail at runtime. Luckily, most OSGi installations have a core which already exports most of this stuff so just put it into your import-package as optional like so (just an example):
org.w3c.dom;resolution:=optional,org.xml.*;resolution:=optional,javax.*;resolution:=optional,* - Services in the manifest are deprecated - As of OSGi R4, listing services (using export/import-services) is deprecated. All exports/imports are handled as packages now and the registration of the services is done in the activator.
- Felix shell access inside Sling - There is a felix remote shell which can be used to access the felix shell service. It can be installed by just installing a couple bundles (shell, shell remote). This will allow you to run felix shell commands inside Sling via telnet.
- Checkout the sample bundle code:
svn co https://source.sakaiproject.org/contrib/caret/osgi-eb/tags/eb-1.0/ osgi-eb - Build the bundle using maven 2:
cd osgi-eb
mvn clean install - Use the Felix console for bundles to install and start the eb-services and eb-rest bundles:
(should be eb-component/target/eb-services-1.0.jar and eb-webapp/target/eb-rest-1.0.jar) - Verify they started by refreshing and making sure they appear to be running:
For some reason there are no errors logged to the console by default when a bundle fails to start and no messages appear in the web interface. As a result you simply get a silent failure where it seems to have worked but is actually just installed and inactive. The bundle should say Active next to it (make sure you refresh).
NOTE: The errors will be logged into SLING_HOME/logs/error.log - Go to the URL that is setup for the bundles (http://localhost:8080/eb), it should load up the description page for the rest webapp portion
Here is the start method from the EB services activator showing an example of how services are registered and tracked. This also demonstrates one way to track things which are used by our services. Pre-OSGi Java programming might require the EntityProvider to be manually registered with the EB system using a register method. By leveraging OSGi's ability to lookup services, we can instead simply have developers create their providers and register them with OSGi as services. Then the eb-services bundle picks up the registered providers and handles the registration automatically.
public void start(BundleContext context) throws Exception {
System.out.println("INFO: Starting EB module");
// Create the EB core services
coreServiceManager = new EntityBrokerCoreServiceManager();
// register trackers to handle the optional services
eipTracker = new ServiceTrackerPlus<ExternalIntegrationProvider>(context, ExternalIntegrationProvider.class) {
@Override
protected void serviceUpdate(ServiceEvent event, ExternalIntegrationProvider service)
throws Exception {
coreServiceManager.getEntityBrokerManager().setExternalIntegrationProvider(getService());
}
};
// register a tracker for the entity providers (will find them as osgi services and handle registration)
providerTracker = new ServiceTrackerPlus<EntityProvider>(context, EntityProvider.class) {
@Override
protected void serviceUpdate(ServiceEvent event, EntityProvider service)
throws Exception {
EntityProvider provider = getService(event);
if (event.getType() == ServiceEvent.UNREGISTERING) {
coreServiceManager.getEntityProviderManager().unregisterEntityProvider(provider);
} else {
coreServiceManager.getEntityProviderManager().registerEntityProvider(provider);
}
}
};
// look up a service we optionally want to use but do not require
ServiceTrackerPlus<DeveloperHelperService> dhsTracker =
new ServiceTrackerPlus<DeveloperHelperService>(context, DeveloperHelperService.class);
DeveloperHelperService dhs = dhsTracker.getService();
System.out.println("DeveloperHelperService is currently: " + dhs);
// register the core services from this bundle
ebRegistration = context.registerService( EntityBroker.class.getName(), coreServiceManager.getEntityBroker(), null);
ebManagerRegistration = context.registerService( EntityBrokerManager.class.getName(), coreServiceManager.getEntityBrokerManager(), null);
ebProviderManagerRegistration = context.registerService( EntityProviderManager.class.getName(), coreServiceManager.getEntityProviderManager(), null);
ebEVAPManagerRegistration = context.registerService( EntityViewAccessProviderManager.class.getName(), coreServiceManager.getEntityViewAccessProviderManager(), null);
ebHSAPManagerRegistration = context.registerService( HttpServletAccessProviderManager.class.getName(), coreServiceManager.getHttpServletAccessProviderManager(), null);
System.out.println("INFO: Started EB module and registered services");
}
In this code example, we see the code to handle the start of the EB rest activator. This allows the eb-services bundle to be stopped and started without having to manually do anything to the rest bundle (whose services depend on the ones in the eb-services bundle). It will shutdown the rest services until the core services it requires are available again.
public void start(BundleContext context) throws Exception {
System.out.println("INFO: Starting EB ReST module");
// initialize tracker
this.requiredServicesTracker = new ServicesTracker(context, HttpService.class, EntityBrokerManager.class) {
@Override
protected void requiredServicesReady(Object service, ServiceTracker2 changed,
Map<String, ServiceTracker2> serviceTrackers) throws Exception {
// required services are ready so startup
startServices(getService(HttpService.class), getService(EntityBrokerManager.class));
}
@Override
protected void requiredServicesChanged(Object service, ServiceTracker2 changed,
Map<String, ServiceTracker2> serviceTrackers) throws Exception {
// required services changed so stop and restart
stopServices(getService(HttpService.class));
startServices(getService(HttpService.class), getService(EntityBrokerManager.class));
}
@Override
protected void requiredServicesDropped(Object service, ServiceTracker2 changed,
Map<String, ServiceTracker2> serviceTrackers) throws Exception {
// required services gone so shutdown
stopServices(getService(HttpService.class));
}
};
// optional services trackers
dhsTracker = new ServiceTrackerPlus<DeveloperHelperService>(context, DeveloperHelperService.class) {
@Override
protected void serviceUpdate(ServiceEvent event, DeveloperHelperService service)
throws Exception {
DeveloperHelperService dhs = getService();
if (servlet != null) {
servlet.updateDHS(dhs);
}
}
};
// TODO need to handle the case of the hsapm changing?
hsapmTracker = new ServiceTrackerPlus<HttpServletAccessProviderManager>(context, HttpServletAccessProviderManager.class);
// register the servlet if services are ready
this.requiredServicesTracker.startCheck();
}
Here are some more links to OSGi materials that may be helpful:
http://neilbartlett.name/blog/osgi-articles/
http://www.osgi.org/About/HowOSGi
NOTE: Updated for the graduation of sling and changes in URLs that resulted
No comments:
Post a Comment