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

org.glassfish.osgiweb.OSGiWebDeploymentContext Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.osgiweb;

import org.apache.naming.resources.WebDirContext;
import org.glassfish.osgijavaeebase.OSGiArchiveHandler;
import org.glassfish.osgijavaeebase.OSGiDeploymentContext;
import org.glassfish.osgijavaeebase.BundleClassLoader;
import org.glassfish.internal.api.Globals;
import org.glassfish.internal.api.ClassLoaderHierarchy;
import org.glassfish.web.loader.WebappClassLoader;
import org.glassfish.api.ActionReport;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.api.deployment.OpsParams;
import org.osgi.framework.*;

import java.io.FileFilter;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.IOException;
import java.io.File;

import com.sun.enterprise.module.common_impl.CompositeEnumeration;

/**
 * This is at the heart of WAB support. It is responsible for setting up
 * a class loader for the WAB. In theory, a WAB's class loader should just be a simple wrapper around
 * the Bundle object, but in truth we need to take care of all the special requirements mostly
 * around resource finding logic to ensure a WAB behaves like a WAR in our web container. So,
 * we create a special class loader called {@link org.glassfish.osgiweb.OSGiWebDeploymentContext.WABClassLoader}
 * and set that in the deployment context.
 *
 * @author [email protected]
 */
class OSGiWebDeploymentContext extends OSGiDeploymentContext {

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

    public OSGiWebDeploymentContext(ActionReport actionReport,
                                            Logger logger,
                                            ReadableArchive source,
                                            OpsParams params,
                                            ServerEnvironment env,
                                            Bundle bundle) throws Exception {
        super(actionReport, logger, source, params, env, bundle);
        // ArchiveHandler must correctly return the ArchiveType for DOL processing to succeed,
        setArchiveHandler(new OSGiArchiveHandler(){
//            @Override
            public List getClassPathURIs(ReadableArchive archive) {
                final List uris = new ArrayList();
                File base = getSourceDir();
                assert (base != null && base.isDirectory());
                uris.add(new File(base, "WEB-INF/classes/").toURI());
                new File(base, "WEB-INF/lib/").listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File pathname) {
                        if (pathname.isFile() && pathname.getName().endsWith(".jar")) {
                            uris.add(pathname.toURI());
                        }
                        return false;
                    }
                });
                OSGiWebDeploymentContext.logger.logp(Level.INFO, "OSGiWebDeploymentContext", "getClassPathURIs",
                        "uris = {0}", new Object[]{uris});
                return uris;
            }

            @Override
            public String getArchiveType() {
                // Since I am not able to reference GF 4.0 APIs as they are not yet staged in a maven repo,
                // I am accessing the value in a round about way.
                return javax.enterprise.deploy.shared.ModuleType.WAR.toString(); // WarType.ARCHIVE_TYPE;
            }
        });
    }

    protected void setupClassLoader() throws Exception     {
        finalClassLoader = new WABClassLoader(null);
        shareableTempClassLoader = finalClassLoader;
        WebappClassLoader.class.cast(finalClassLoader).start();
    }

    private class WABClassLoader extends WebappClassLoader {
        /*
         * We need this class loader for variety of reasons explained below:
         * a) GlassFish default servlet (DefaultServlet.java), the servlet responsible for serving static content
         * fails to serve any static content from META-INF/resources/ of WEB-INF/lib/*.jar, if the classloader is not
         * an instanceof WebappClassLoader.
         * b) DefaultServlet also expects WebappClassLoader's resourceEntries to be properly populated.
         * c) JSPC relies on getURLs() methods so that it can discover TLDs in the web app. Setting up
         * repositories and jar files ensures that WebappClassLoader's getURLs() method will
         * return appropriate URLs for JSPC to work.
         * d) set a specialized FileDirContext object that restricts access to OSGI-INF and OSGI-OPT resources of a WAB
         * as required by the OSGi WAB spec.
         *
         * It overrides loadClass(), getResource() and getResources() as opposed to
         * their findXYZ() equivalents so that the OSGi export control mechanism
         * is enforced even for classes and resources available in the system/boot class loader.
         * The only time this class loader is defining class loader for some classes is when this class loader
         * is used by containers like CDI or EJB to define generated classes.
         */

        final BundleClassLoader delegate1 = new BundleClassLoader(bundle);
        final ClassLoader delegate2 =
                Globals.get(ClassLoaderHierarchy.class).getAPIClassLoader();

        @Override
        public Class loadClass(String name) throws ClassNotFoundException {
            return loadClass(name, false);
        }

        @Override
        protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException
        {
            Class c = findLoadedClass(name); // this class loader may be the defining loader for a proxy or generated class 
            if (c != null) return c;
            // mojarra uses Thread's context class loader (which is us) to look up custom annotation provider.
            // since we don't export our package and in fact hide our provider, we need to load them using
            // current loader.
            if (hiddenServices.contains(name)) {
                return Class.forName(name);
            }
            try {
                return delegate1.loadClass(name, resolve);
            } catch (ClassNotFoundException cnfe) {
                return delegate2.loadClass(name);
            }
        }

        @Override
        public URL getResource(String name)
        {
            URL url = delegate1.getResource(name);
            if (url == null) {
                url = delegate2.getResource(name);
            }
            return url;
        }

        @Override
        public Enumeration getResources(String name) throws IOException
        {
            List> enumerators = new ArrayList>();
            final String mappedResourcePath = hiddenServicesMap.get(name);
            if (mappedResourcePath != null) {
                return getClass().getClassLoader().getResources(mappedResourcePath);
            }
            enumerators.add(delegate1.getResources(name));
            enumerators.add(delegate2.getResources(name));
            return new CompositeEnumeration(enumerators);
        }

        @Override
        public InputStream getResourceAsStream(String name) {
            // We need to override this method because of the stupid WebappClassLoader that for some reason
            // not only overrides getResourceAsStream, it also does not delegate to getResource method.
            URL url = getResource(name);
            try {
                return url != null ? url.openStream() : null;
            } catch (IOException e) {
                return null;
            }
        }

        public WABClassLoader(ClassLoader parent) {
            super(parent);
            setDelegate(true); // we always delegate. The default is false in WebappClassLoader!!!

            File base = getSourceDir();
            // Let's install a customized dir context that does not allow static contents from
            // OSGI-OPT and OSGI-INF directories as required by the OSGi WAB spec.
            WebDirContext r = new OSGiWebDirContext();
            r.setDocBase(base.getAbsolutePath());
            setResources(r);

            // add WEB-INF/classes/ and WEB-INF/lib/*.jar to repository list, because many legacy code
            // path like DefaultServlet, JSPC, StandardContext rely on them.
            // See WebappLoader.setClassPath() for example.
            addRepository("WEB-INF/classes/", new File(base, "WEB-INF/classes/"));
            File libDir = new File(base, "WEB-INF/lib");
            if (libDir.exists()) {
                int baseFileLen = base.getPath().length();
                for (File file : libDir.listFiles(
                        new FileFilter() {
                            public boolean accept(File pathname) {
                                String fileName = pathname.getName();
                                return (fileName.endsWith(".jar") && pathname.isFile());
                            }
                        }))
                {
                    try {
                    	addJar(file.getPath().substring(baseFileLen), new JarFile(file), file);
                        // jarFile is closed by WebappClassLoader.stop()
                    } catch (Exception e) {
                        // Catch and ignore any exception in case the JAR file
                        // is empty.
                    }
                }
            }
            setWorkDir(getScratchDir("jsp")); // We set the same working dir as set in WarHandler
        }

    }

    /**
     * We don't package our custom providers as a META-INF/services/, for doing so will make them
     * visible to non hybrid applications as well. So, we package it at a different location and
     * punch in our classloader appropriately. This map holds the key name that client is looking for
     * and the value is where we have placed it in our bundle.
     */
    private static Map hiddenServicesMap;

    /**
     * Since mojarra uses thread's context class loader to look up custom providers and our custom providers
     * are not available via APIClassLoader's META-INF/service punch-in mechanism, we need to make them visible
     * specially. This field maintains a list of such service class names.
     * As much as we would like to hide {@link org.glassfish.osgiweb.OSGiWebModuleDecorator}, we can't, because
     * that's looked up via habitat, which means it has to be either present as META-INF/services in the bundle itself
     * or added as an existing inhabitant. We have gone for the latter approach for the decorator. The other providers
     * that are looked up by mojarra are hidden using the technique implemented here.
     */
    private static Collection hiddenServices;
    static {
        Map map = new HashMap();

        // This is for the custom AnnotationProvider. Note that Mojarra surprising uses different nomenclature than
        // what is used by JDK SPI. The service type is AnnotationProvider, yet it looks for annotationprovider.
        map.put("META-INF/services/com.sun.faces.spi.annotationprovider",
                "META-INF/hiddenservices/com.sun.faces.spi.annotationprovider");

        // This is for our custom faces-config.xml discoverer
        map.put("META-INF/services/com.sun.faces.spi.FacesConfigResourceProvider",
                "META-INF/hiddenservices/com.sun.faces.spi.FacesConfigResourceProvider");

        // This is for our custom taglib.xml discoverer
        map.put("META-INF/services/com.sun.faces.spi.FaceletConfigResourceProvider",
                "META-INF/hiddenservices/com.sun.faces.spi.FaceletConfigResourceProvider");
        hiddenServicesMap = Collections.unmodifiableMap(map);

        hiddenServices = Collections.unmodifiableList(Arrays.asList(
                OSGiFacesAnnotationScanner.class.getName(),
                OSGiFaceletConfigResourceProvider.class.getName(),
                OSGiFacesConfigResourceProvider.class.getName()
        ));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy