Skip to main content

How to embed GlassFish in an existing OSGi runtime?

Posted by ss141213 on February 14, 2010 at 8:21 AM PST

Typical users of GlassFish use GlassFish in a separate process and they start GlassFish by using commands like the following:

java -jar glassfish.jar
                 or
asadmin start-domain

The above commands first launch an OSGi framework and then deploy the necessary bundles. What if you want to embed GlassFish in an existing OSGi runtime? The bootstrapping code was slightly complicated IMO. Recently when Apache Camel committer Charles Moulliard asked me some questions around embedding GlassFish v3 in an OSGi runtime, I decided to revisit the bootstrap module in order to simplify it so that I had less explaining to do. I recently did exactly that. In this blog, I show how using a simple BundleActivator, one can embed GlassFish in an OSGi runtime. In the use case below, I first start an instance of Equinox (plain vanilla Equinox runtime) and deploy a test bundle which controls life cycle of GlassFish. You can use other launchers like karaf as well.

/tmp/equinox-3.5.1$ java -jar org.eclipse.osgi_3.5.1.R35x_v20090827.jar -console

osgi> install file:///tmp/embeddedgf-1.0-SNAPSHOT.jar
Bundle
id is 1

osgi> start 1
// The above command will start glassfish. You shall some output indicating the same.
// After GlassFish is started, you can use all regular GlassFish commands.
// e.g., to deploy an app, you can run "asadmin deploy ..."

osgi> stop 1
// This will stop GlassFish server. To start again, "start bundle #1"

What is this embeddedgf-1.0-SNAPSHOT.jar?

That's a tiny sample bundle I developed to embed GlassFish. It has a single class called GFActivator. This bundle activator does the following in the start() method:

1. Goes through glassfish modules directory and installs all bundles.

2. It then configures a configuration object with service PID com.sun.enterprise.glassfish.bootstrap.GlassFish. This is the PID for the ManagedService registered by GlassFish.  The PID name is subject to change. It then updates the configuration object with the following properties:

com.sun.aas.installRoot = /space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/
com.sun.aas.instanceRoot =  /space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/domains/domain1/

These two properties are enough for GlassFish to start.

3. It then locates GlassFish main bundle and starts it.

When I stop this bundle, it stops GlassFish server by stopping the main GlassFish bundle. Of course, you can restart the bundle and GlassFish will be restarted. The java source and pom.xml are all part of this zip file.

GFActivator.java

package sahoo.embeddedgf;

import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.Configuration;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;

/**
 * Activator that starts and stops GlassFish.
 * To start glassfish, it does the following:
 * 1. Installs all bundles located in glassfish modules directory.
 * 2. Creates a configuration with a couple of properties:
 * com.sun.aas.installRoot and com.sun.aas.instanceRoot.
 * The former one refers to the directory where glassfish is installed.
 * (e.g., /tmp/glassfishv3/glassfish)
 * The latter one refers to the domain directory - this is a directory containing
 * configuration information and deployed applications, etc.
 * 3. Starts the main bundle of GlassFish. Main bundle is identified by Bundle-SymbolicName
 * org.glassfish.core.glassfish. This main bundle then is used to start any necessary bundle.
 *
 * In order to stop glassfish, we just stop the main bundle.
 *
 * @author Sanjeeb.Sahoo@Sun.COM
 */
public class GFActivator implements BundleActivator
{
    private BundleContext bundleContext;
    // I have hardcoded the glassfish path as this is just an example bundle.
    private String installRoot = "/space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/";
    private String instanceRoot = "/space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/domains/domain1/";
    private Bundle mainBundle;
    private static final String mainBundleName = "org.glassfish.core.glassfish";
    private static final String gfpid = "com.sun.enterprise.glassfish.bootstrap.GlassFish";
    private static final String JAR_EXT = ".jar";

    public void start(BundleContext bundleContext) throws Exception
    {
        this.bundleContext = bundleContext;
        // Install all gf bundles
        installBundles();
        configureBundles();
        startBundles();
    }

    public void stop(BundleContext bundleContext) throws Exception
    {
        if (mainBundle != null) {
            mainBundle.stop(Bundle.STOP_TRANSIENT);
        }
    }

    /**
     * Install all bundles located in modules directory and subdirectories under that.
     */
    private void installBundles() {
        for (File jar : findJars()) {
            try {
                bundleContext.installBundle(jar.toURI().toString());
            } catch (BundleException e) {
                // continue processing after logging the exception
                e.printStackTrace();
            }
        }
    }

    /**
     * Returns a list of jar files found in modules directory and subdirectories under that.
     * Any file with extension .jar is considered as a jar file.
     * @return a list of jar files
     */
    private List findJars() {
        File modulesDir = new File(installRoot, "modules/");
        final List jars = new ArrayList();
        modulesDir.listFiles(new FileFilter(){
            public boolean accept(File pathname) {
                if (pathname.isDirectory()) {
                    pathname.listFiles(this);
                } else if (pathname.getName().endsWith(JAR_EXT)){
                    jars.add(pathname);
                }
                return false;
            }
        });
        return jars;
    }

    private void configureBundles() {
        final Properties props = new Properties();
        props.setProperty("com.sun.aas.installRoot", installRoot);
        props.setProperty("com.sun.aas.instanceRoot", instanceRoot);
        ServiceTracker tracker = new ServiceTracker(bundleContext, ConfigurationAdmin.class.getName(), null) {
            @Override
            public Object addingService(ServiceReference reference) {
                ConfigurationAdmin ca = ConfigurationAdmin.class.cast(bundleContext.getService(reference));
                try {
                    Configuration c = ca.getConfiguration(gfpid, null);
                    c.update(props);
                    close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return super.addingService(reference); //TODO(Sahoo): Not Yet Implemented
            }
        };
        tracker.open();
    }

    /**
     * Starts GlassFish primordial bundle
     */
    private void startBundles() {
        for (Bundle b : bundleContext.getBundles()) {
            if (mainBundleName.equals(b.getSymbolicName())) {
                mainBundle = b;
                try {
                    b.start(Bundle.START_TRANSIENT);
                } catch (BundleException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
 

Happy GlassFish-ing.

Statutory Warning: The code shown here is all sample code.

Comments

I am attempting to install Glassfish within a running ...

I am attempting to install Glassfish within a running instance of Apache Karaf, which is in turn running Equinox. I have replicated the code you have provided above, and have successfully installed and started the bundle. All of the GlassFish related bundles appear to get installed, with some bundles starting automatically. I am able to access the http://localhost:8080 startup page, but when I attempt to access to the Glassfish Administration console at http://localhost:4848, the page tells me it is loading the Administrative Console, and just sits there. It never actually navigates to the login page or the home page of the Administration console.

I have also attempted to simply install glassfish.jar into the OSGi container, and effectively get the same result. When I look in the Karaf logs, I am seeing the exception below. It seems strange, because some aspects of the server are apparently running, as I can access some of the REST management services at http://localhost:4848/management/domain and can successfully query information about the running server.

Any help you could provide would be greatly appreciated.

2012-01-31 14:28:10,892 | ERROR | Thread-49 | server | e.v3.server.ApplicationLifecycle 479 | - - | injection fai
led on class com.sun.enterprise.deployment.archivist.WebArchivist from org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@322abfc3
java.lang.LinkageError: injection failed on class com.sun.enterprise.deployment.archivist.WebArchivist from org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@322abfc3 at org.jvnet.hk2.component.InjectionManager.inject(InjectionManager.java:254)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.jvnet.hk2.component.InjectionManager.inject(InjectionManager.java:91)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.AbstractCreatorImpl.inject(AbstractCreatorImpl.java:126)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.ConstructorCreator.initialize(ConstructorCreator.java:91)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.AbstractCreatorImpl.get(AbstractCreatorImpl.java:82)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.EventPublishingInhabitant.get(EventPublishingInhabitant.java:139)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.AbstractInhabitantImpl.get(AbstractInhabitantImpl.java:76)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.jvnet.hk2.component.Habitat.getBy(Habitat.java:1048)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.jvnet.hk2.component.Habitat.getByType(Habitat.java:1029)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.jvnet.hk2.component.Habitat.getComponent(Habitat.java:779)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.enterprise.web.WebContainer.loadSystemDefaultWebModules(WebContainer.java:1422)
at com.sun.enterprise.web.WebContainer.postConstruct(WebContainer.java:573)
at com.sun.hk2.component.AbstractCreatorImpl.inject(AbstractCreatorImpl.java:131)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.ConstructorCreator.initialize(ConstructorCreator.java:91)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.AbstractCreatorImpl.get(AbstractCreatorImpl.java:82)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.SingletonInhabitant.get(SingletonInhabitant.java:67)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.EventPublishingInhabitant.get(EventPublishingInhabitant.java:139)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.AbstractInhabitantImpl.get(AbstractInhabitantImpl.java:76)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.glassfish.internal.data.EngineInfo.getContainer(EngineInfo.java:93)[222:org.glassfish.common.internal-api:3.1.1]
at com.sun.enterprise.v3.server.ApplicationLifecycle.startContainers(ApplicationLifecycle.java:957)[273:org.glassfish.core.kernel:3.1.1]
at com.sun.enterprise.v3.server.ApplicationLifecycle.setupContainerInfos(ApplicationLifecycle.java:667)[273:org.glassfish.core.kernel:3.1.1]
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:368)[273:org.glassfish.core.kernel:3.1.1]
at com.sun.enterprise.v3.server.ApplicationLoaderService.processApplication(ApplicationLoaderService.java:375)[273:org.glassfish.core.kernel:3
.1.1]
at com.sun.enterprise.v3.admin.adapter.InstallerThread.load(InstallerThread.java:210)[273:org.glassfish.core.kernel:3.1.1]
at com.sun.enterprise.v3.admin.adapter.InstallerThread.run(InstallerThread.java:108)[273:org.glassfish.core.kernel:3.1.1]
Caused by: java.lang.NoClassDefFoundError: javax/annotation/ManagedBean
at com.sun.enterprise.deployment.annotation.handlers.ManagedBeanHandler.getAnnotationType(ManagedBeanHandler.java:75)
at org.glassfish.apf.impl.AnnotationProcessorImpl.pushAnnotationHandler(AnnotationProcessorImpl.java:399)
at com.sun.enterprise.deployment.annotation.factory.SJSASFactory.postConstruct(SJSASFactory.java:91)
at com.sun.hk2.component.AbstractCreatorImpl.inject(AbstractCreatorImpl.java:131)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.ConstructorCreator.initialize(ConstructorCreator.java:91)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.AbstractCreatorImpl.get(AbstractCreatorImpl.java:82)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.SingletonInhabitant.get(SingletonInhabitant.java:67)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.EventPublishingInhabitant.get(EventPublishingInhabitant.java:139)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.AbstractInhabitantImpl.get(AbstractInhabitantImpl.java:76)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.jvnet.hk2.component.Habitat.getBy(Habitat.java:1048)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.jvnet.hk2.component.Habitat.getByType(Habitat.java:1029)[70:com.sun.enterprise.auto-depends:1.1.6]
at com.sun.hk2.component.InjectInjectionResolver.getComponentInjectValue(InjectInjectionResolver.java:159)[70:com.sun.enterprise.auto-depends:
1.1.6]
at com.sun.hk2.component.InjectInjectionResolver.getValue(InjectInjectionResolver.java:90)[70:com.sun.enterprise.auto-depends:1.1.6]
at org.jvnet.hk2.component.InjectionManager.inject(InjectionManager.java:141)[70:com.sun.enterprise.auto-depends:1.1.6]
... 24 more
Caused by: java.lang.ClassNotFoundException: javax.annotation.ManagedBean
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:460)[osgi-3.6.2.R36x_v20110210.jar:]
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:422)[osgi-3.6.2.R36x_v20110210.jar:]
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:410)[osgi-3.6.2.R36x_v20110210.jar:]
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)[osgi-3.6.2.R36x_v20110210.jar:]
at java.lang.ClassLoader.loadClass(Unknown Source)[:1.7.0_02]
... 38 more
2012-01-31 14:28:10,970 | ERROR | Thread-49 | server | .server.ApplicationLoaderService 383 | - - | Exception whi
le deploying the app [__admingui]

Jim, Yes, it is sufficient to just install and start ...

Jim,

Yes, it is sufficient to just install and start glassfish.jar modules to embed GlassFish. You can even use checkout the sample code available here [1] which can download GlassFish from maven and install it for you.

The exception suggests that somehow admin console is wired to wrong javax.annotation package. This package exists in JDK 6 as well as distributed in glassfish/modules/endorsed/javax.annotation.jar. The one in GlassFish has javax.annotation.ManagedBean.class, but JDK has older package. Could you check to see which javax.annotation package is getting used and from where. It could be an issue in the way Karaf is configured to export javax.annotation package.

Pl. post questions to users@glassfish.java.net so that others can benefit from the discussion.

Thanks,

Sahoo

[1] https://svn.java.net/svn/glassfish~svn/trunk/fighterfish/sample/embedded...