All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.osgiejb.OSGiEJBDeployer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.osgiejb;

import com.sun.enterprise.deploy.shared.ArchiveFactory;
import com.sun.enterprise.deployment.Application;
import org.glassfish.api.ActionReport;
import org.glassfish.internal.data.ApplicationInfo;
import org.glassfish.internal.deployment.Deployment;
import org.glassfish.osgijavaeebase.*;
import org.glassfish.server.ServerEnvironmentImpl;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author [email protected]
 */
public class OSGiEJBDeployer extends AbstractOSGiDeployer {

    private static Logger logger = Logger.getLogger(OSGiEJBDeployer.class.getPackage().getName());

    private EJBTracker ejbTracker;

    private final InitialContext ic;

    public OSGiEJBDeployer(BundleContext ctx) {
        super(ctx, Integer.MIN_VALUE);
        try {
            ic = new InitialContext();
        } catch (NamingException e) {
            throw new RuntimeException(e); // TODO(Sahoo): Proper Exception Handling
        }
        ejbTracker = new EJBTracker();
        ejbTracker.open(true);
    }

    public OSGiUndeploymentRequest createOSGiUndeploymentRequest(Deployment deployer, ServerEnvironmentImpl env, ActionReport reporter, OSGiApplicationInfo osgiAppInfo) {
        return new OSGiEJBUndeploymentRequest(deployer, env, reporter, osgiAppInfo);
    }

    public OSGiDeploymentRequest createOSGiDeploymentRequest(Deployment deployer, ArchiveFactory archiveFactory, ServerEnvironmentImpl env, ActionReport reporter, Bundle b) {
        return new OSGiEJBDeploymentRequest(deployer, archiveFactory, env, reporter, b);
    }

    public boolean handles(Bundle bundle) {
        return isEJBBundle(bundle);
    }

    /**
     * Determines if a bundle represents a EJB application or not.
     * We determine this by looking at presence of Application-Type manifest header.
     *
     * @param b
     * @return
     */
    private boolean isEJBBundle(Bundle b) {
        final Dictionary headers = b.getHeaders();
        return headers.get(Constants.EXPORT_EJB) != null &&
                headers.get(org.osgi.framework.Constants.FRAGMENT_HOST) == null;
    }

    /**
     * An EJBTracker is responsible for registering the desired EJBs in OSGi service registry.
     * It is only applicable for OSGi enabled EJB bundles. Everytime such a bundle gets
     * deployed, OSGiContainer registers an {@link org.glassfish.osgijavaeebase.OSGiApplicationInfo}.
     * This class tracks such an object and queries its manifest for {@link Constants#EXPORT_EJB} header.
     * Based on the value of the header, it selects EJBs to be registered as OSGi services. To keep the
     * implementation simple at this point, we only support mapping of stateless EJBs with local business interface
     * views to OSGi services. When an EJB is registered as service, the service properties include
     * the portable JNDI name of the EJB in a service property names {@link #JNDI_NAME_PROP}.
     * All the services are registered under the bundle context of the OSGi/EJB bundle which hosts the EJBs.
     * While registering the EJBs, thread's context class loader is also set to the application class loader of
     * the OSGi/EJB bundle application so that any service tracker (like CDI producer methods) listening
     * to service events will get called in an appropriate context.
     */
    class EJBTracker extends ServiceTracker {
        // TODO(Sahoo): More javadoc needed about service properties and service registration
        private final String JNDI_NAME_PROP = "jndi-name";

        /**
         * Maps bundle id to service registrations
         */
        private Map> b2ss =
                new ConcurrentHashMap>();
        private ServiceRegistration reg;

        EJBTracker() {
            super(getBundleContext(), OSGiApplicationInfo.class.getName(), null);
        }

        @Override
        public Object addingService(ServiceReference reference) {
            OSGiApplicationInfo osgiApplicationInfo = OSGiApplicationInfo.class.cast(context.getService(reference));
            String exportEJB = (String) osgiApplicationInfo.getBundle().getHeaders().get(Constants.EXPORT_EJB);
            if (exportEJB != null) {
                // remove spaces. I once spent 1 hour trying to debug why EJB was not getting registered
                // and it turned out that user had specified "ALL " in the manifest.
                exportEJB = exportEJB.trim();
                ApplicationInfo ai = osgiApplicationInfo.getAppInfo();
                Application app = ai.getMetaData(Application.class);
                Collection ejbs = DolAdapter.convert(app.getEjbDescriptors());
                logger.info("addingService: Found " + ejbs.size() + " no. of EJBs");
                Collection ejbsToBeExported = new ArrayList();
                if (Constants.EXPORT_EJB_ALL.equals(exportEJB)) {
                    ejbsToBeExported = ejbs;
                } else if (Constants.EXPORT_EJB_NONE.equals(exportEJB)) {
                    logger.info("addingService: Skipping adding EJBs as OSGi services as per configuration");
                } else {
                    StringTokenizer st = new StringTokenizer(exportEJB, ",");
                    while (st.hasMoreTokens()) {
                        String next = st.nextToken();
                        for (DolAdapter.EjbDescriptor ejb : ejbs) {
                            if (next.equals(ejb.getName())) {
                                ejbsToBeExported.add(ejb);
                            }
                        }
                    }
                }
                b2ss.put(osgiApplicationInfo.getBundle().getBundleId(), new ArrayList());
                ClassLoader oldTCC = switchTCC(osgiApplicationInfo);
                try {
                    for (DolAdapter.EjbDescriptor ejb : ejbsToBeExported) {
                        registerEjbAsService(ejb, osgiApplicationInfo.getBundle());
                    }
                } finally {
                    Thread.currentThread().setContextClassLoader(oldTCC);
                }
            }
            return osgiApplicationInfo;
        }

        /**
         *
         * @param osgiApplicationInfo application which just got deployed
         * @return the old thread context classloader
         */
        private ClassLoader switchTCC(OSGiApplicationInfo osgiApplicationInfo) {
            ClassLoader newTCC = osgiApplicationInfo.getClassLoader();
            final Thread thread = Thread.currentThread();
            ClassLoader oldTCC = thread.getContextClassLoader();
            thread.setContextClassLoader(newTCC);
            return oldTCC;
        }

        private void registerEjbAsService(DolAdapter.EjbDescriptor ejb, Bundle bundle) {
            System.out.println(ejb);
            try {
                if (com.sun.enterprise.deployment.EjbSessionDescriptor.TYPE.equals(ejb.getType())) {
                    DolAdapter.EjbSessionDescriptor sessionBean = DolAdapter.EjbSessionDescriptor.class.cast(ejb);
                    if (com.sun.enterprise.deployment.EjbSessionDescriptor.STATEFUL.equals(sessionBean.getSessionType())) {
                        logger.warning("Stateful session bean can't be registered as OSGi service");
                    } else {
                        final BundleContext ejbBundleContext = bundle.getBundleContext();
                        for (String lbi : sessionBean.getLocalBusinessClassNames()) {
                            String jndiName = sessionBean.getPortableJndiName(lbi);
                            Object service = null;
                            try {
                                service = ic.lookup(jndiName);
                            } catch (NamingException e) {
                                e.printStackTrace();
                            }
                            Properties props = new Properties();
                            props.put(JNDI_NAME_PROP, jndiName);

                            // Note: we register using the bundle context of the bundle containing the EJB.
                            reg = ejbBundleContext.registerService(lbi, service, props);
                            b2ss.get(bundle.getBundleId()).add(reg);
                        }
                    }
                } else {
                    logger.warning("Only stateless bean or singleton beans can be registered as OSGi service");
                }
            } catch (Exception e) {
                logger.logp(Level.SEVERE, "OSGiEJBDeployer$EJBTracker", "registerEjbAsService", "Exception registering service for ejb by name", e);
            }
        }

        @Override
        public void removedService(ServiceReference reference, Object service) {
            /*
             * When the OSGi-EJB container goes away, the ejb bundle remains in ACTIVE state, so
             * we must unregister the services that OSGi-EJB container has registered on that bundle's behalf.
             */

            OSGiApplicationInfo osgiApplicationInfo = OSGiApplicationInfo.class.cast(service);
            ApplicationInfo ai = osgiApplicationInfo.getAppInfo();
            Application app = ai.getMetaData(Application.class);
            Collection ejbs = DolAdapter.convert(app.getEjbDescriptors());
            logger.info("removedService: Found " + ejbs.size() + " no. of EJBs");
            final Collection regs = b2ss.get(osgiApplicationInfo.getBundle().getBundleId());
            if (regs != null) { // it can be null if this bundle is not an OSGi-EJB bundle.
                for (ServiceRegistration reg : regs) {
                    if (reg != null) {
                        try {
                            reg.unregister();
                        } catch (Exception e) {
                            // If the underlying bundle is stopped, then the services registered for that context
                            // would have already been unregistered, so an IllegalStateException can be raised here.
                            // log it in FINE level and ignore.
                            logger.logp(Level.FINE, "OSGiEJBDeployer$EJBTracker", "removedService",
                                    "Exception unregistering " + reg, e);
                        }
                    }
                }
            }
            super.removedService(reference, service);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy