Package org.apache.tomee.catalina

Source Code of org.apache.tomee.catalina.OpenEJBContextConfig

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
    * (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomee.catalina;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.deploy.ContextResource;
import org.apache.catalina.deploy.NamingResources;
import org.apache.catalina.deploy.WebXml;
import org.apache.catalina.startup.ContextConfig;
import org.apache.naming.factory.Constants;
import org.apache.openejb.assembler.classic.AppInfo;
import org.apache.openejb.assembler.classic.ClassListInfo;
import org.apache.openejb.assembler.classic.OpenEjbConfiguration;
import org.apache.openejb.assembler.classic.ResourceInfo;
import org.apache.openejb.assembler.classic.WebAppBuilder;
import org.apache.openejb.assembler.classic.WebAppInfo;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.config.ServiceUtils;
import org.apache.openejb.loader.IO;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import org.apache.openejb.util.URLs;
import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
import org.apache.tomcat.util.bcel.classfile.ElementValuePair;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomee.common.NamingUtil;
import org.apache.tomee.common.ResourceFactory;

import javax.servlet.ServletContainerInitializer;
import javax.ws.rs.core.Application;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class OpenEJBContextConfig extends ContextConfig {
    private static Logger logger = Logger.getInstance(LogCategory.OPENEJB, OpenEJBContextConfig.class);

    private static final String MYFACES_TOMEEM_CONTAINER_INITIALIZER = "org.apache.tomee.myfaces.TomEEMyFacesContainerInitializer";
    private static final String TOMEE_MYFACES_CONTEXT_LISTENER = "org.apache.tomee.myfaces.TomEEMyFacesContextListener";
    private static final String ADJUST_DATASOURCE_JNDI_NAMES = SystemInstance.get().getProperty("tomee.resources.adjust-web-xml-jndi-name", "true");

    private TomcatWebAppBuilder.StandardContextInfo info;

    // processAnnotationXXX is called for each folder of WEB-INF
    // since we store all classes in WEB-INF we will do it only once so use this boolean to avoid multiple processing
    private boolean webInfClassesAnnotationsProcessed = false;

    public OpenEJBContextConfig(TomcatWebAppBuilder.StandardContextInfo standardContextInfo) {
        logger.debug("OpenEJBContextConfig({0})", standardContextInfo.toString());
        info = standardContextInfo;
    }

    @Override
    public void configureStart() {
        super.configureStart();
        adjustDataSourceNameIfNecessary(); // doing it here to potentially factorize resource id resolution
        cleanUpRestServlets();
    }

    private void cleanUpRestServlets() {
        final WebAppInfo webAppInfo = info.get();
        final AppInfo appInfo = info.app();
        if (webAppInfo == null || appInfo == null || "false".equalsIgnoreCase(appInfo.properties.getProperty("openejb.jaxrs.on", "true"))) {
            return;
        }

        final Container[] children = context.findChildren();
        final Map<String, Container> mappedChildren = new HashMap<String, Container>();
        if (children != null) {
            // index potential rest containers by class to cleanup applications defined as servlet
            for (Container c : children) {
                if (!(c instanceof StandardWrapper)) {
                    continue;
                }

                final StandardWrapper wrapper = (StandardWrapper) c;

                String appSpec = wrapper.getInitParameter("javax.ws.rs.Application");
                if (appSpec != null) {
                    mappedChildren.put(appSpec, c);
                } else {
                    String app = wrapper.getInitParameter(Application.class.getName());
                    if (app != null) {
                        mappedChildren.put(app, c);
                    } else if (wrapper.getServletClass() == null) {
                        try {
                            if (Application.class.isAssignableFrom(
                                    context.getLoader().getClassLoader().loadClass(wrapper.getServletName()))) {
                                context.removeChild(c); // remove directly since it is not in restApplications
                            }
                        } catch (Exception e) {
                            // no-op
                        }
                    }
                }
            }

            // cleanup
            for (String clazz : webAppInfo.restApplications) {
                final Container child = mappedChildren.get(clazz);
                if (child != null) {
                    context.removeChild(child);
                }
            }
        }
    }

    @Override
    protected void processAnnotationWebServlet(final String className, final AnnotationEntry ae, final WebXml fragment) {
        try {
            super.processAnnotationWebServlet(className, ae, fragment);
        } catch (IllegalArgumentException iae) {
            // otherwise TCKs are not passing, hope to be able to let it with next TCK versions

            String[] urlPatterns = null;
            for (ElementValuePair evp : ae.getElementValuePairs()) {
                String name = evp.getNameString();
                if ("value".equals(name) || "urlPatterns".equals(name)) {
                    urlPatterns = processAnnotationsStringArray(evp.getValue());
                    break;
                }
            }

            if (urlPatterns != null) {
                for (String pattern : urlPatterns) {
                    if (fragment.getServletMappings().containsKey(pattern)) {
                        logger.warning(iae.getMessage(), iae);
                        return;
                    }
                }
            }

            throw iae;
        }
    }

    @Override
    protected void contextConfig(final Digester digester) {
        final NamingResources resources;
        if (context != null) {
            resources = context.getNamingResources();
        } else {
            resources = null;
        }

        if (resources instanceof OpenEJBNamingResource) {
            ((OpenEJBNamingResource) resources).setTomcatResource(true);
        }
        super.contextConfig(digester);
        if (resources instanceof OpenEJBNamingResource) {
            ((OpenEJBNamingResource) resources).setTomcatResource(false);
        }
    }

    private void adjustDataSourceNameIfNecessary() {
        if (context == null || "false".equalsIgnoreCase(ADJUST_DATASOURCE_JNDI_NAMES)) {
            return;
        }

        final NamingResources resources = context.getNamingResources();
        if (resources == null) {
            return;
        }

        final ContextResource[] foundResources = resources.findResources();
        String[] ids = null;
        if (foundResources != null) {
            for (ContextResource resource : foundResources) {
                if ("javax.sql.DataSource".equals(resource.getType())) {
                    String jndiName = (String) resource.getProperty("mappedName");
                    if (jndiName == null) {
                        jndiName = resource.getName();
                    }
                    if (jndiName == null) {
                        continue;
                    }

                    if (ids == null) {
                        final Properties props = new Properties();
                        final OpenEjbConfiguration runningConfig = SystemInstance.get().getComponent(OpenEjbConfiguration.class);
                        final List<String> resourceIds = new ArrayList<String>();
                        if (runningConfig != null) {
                            for (final ResourceInfo resourceInfo : runningConfig.facilities.resources) {
                                if (ConfigurationFactory.isResourceType(resourceInfo.service, resourceInfo.types, "javax.sql.DataSource")
                                        && ServiceUtils.implies(props, resourceInfo.properties)) {
                                    resourceIds.add(resourceInfo.id);
                                }
                            }
                        }
                        ids = resourceIds.toArray(new String[resourceIds.size()]);
                    }

                    String mostMatchingId = null;
                    for (String id : ids) {
                        if (id.equals(jndiName)) {
                            mostMatchingId = jndiName;
                            break;
                        } else if (jndiName.endsWith("/" + id) && mostMatchingId == null) {
                            mostMatchingId = id;
                        }
                    }

                    if (mostMatchingId != null) {
                        resource.setProperty("mappedName", "java:" + mostMatchingId);
                        resource.setProperty(NamingUtil.RESOURCE_ID, "java:" + mostMatchingId);
                        resource.setProperty(Constants.FACTORY, ResourceFactory.class.getName());
                    }
                }
            }
        }
    }

    @Override
    protected WebXml createWebXml() {
        String prefix = "";
        if (context instanceof StandardContext) {
            StandardContext standardContext = (StandardContext) context;
            prefix = standardContext.getEncodedPath();
            if (prefix.startsWith("/")) {
                prefix = prefix.substring(1);
            }
        }
        return new OpenEJBWebXml(prefix);
    }

    public class OpenEJBWebXml extends WebXml {
        public static final String OPENEJB_WEB_XML_MAJOR_VERSION_PROPERTY = "openejb.web.xml.major";

        private String prefix;

        public OpenEJBWebXml(String prefix) {
            this.prefix = prefix;
        }

        @Override
        public int getMajorVersion() {
            return SystemInstance.get().getOptions().get(prefix + "." + OPENEJB_WEB_XML_MAJOR_VERSION_PROPERTY,
                    SystemInstance.get().getOptions().get(OPENEJB_WEB_XML_MAJOR_VERSION_PROPERTY, super.getMajorVersion()));
        }
    }

    @Override
    protected void webConfig() {
        // read the real config
        super.webConfig();

        // add myfaces auto-initializer
        try {
            final Class<?> myfacesInitializer = Class.forName(MYFACES_TOMEEM_CONTAINER_INITIALIZER, true, context.getLoader().getClassLoader());
            final ServletContainerInitializer instance = (ServletContainerInitializer) myfacesInitializer.newInstance();
            context.addServletContainerInitializer(instance, getJsfClasses(context));
            context.addApplicationListener(TOMEE_MYFACES_CONTEXT_LISTENER); // cleanup listener
        } catch (Exception ignored) {
            // no-op
        }
    }

    private Set<Class<?>> getJsfClasses(final Context context) {
        final WebAppBuilder builder = SystemInstance.get().getComponent(WebAppBuilder.class);
        final ClassLoader cl = context.getLoader().getClassLoader();
        final Map<String, Set<String>> scanned = builder.getJsfClasses().get(cl);

        if (scanned == null || scanned.isEmpty()) {
            return null;
        }

        final Set<Class<?>> classes = new HashSet<Class<?>>();
        for (Set<String> entry : scanned.values()) {
            for (String name : entry) {
                try {
                    classes.add(cl.loadClass(name));
                } catch (ClassNotFoundException ignored) {
                    logger.warning("class '" + name + "' was found but can't be loaded as a JSF class");
                }
            }
        }

        return classes;
    }

    @Override // called before processAnnotationsFile so using it as hook to init webInfClassesAnnotationsProcessed
    protected void processServletContainerInitializers(final Set<WebXml> fragments) {
        webInfClassesAnnotationsProcessed = false;
        try {
            super.processServletContainerInitializers(fragments);
        } catch (RuntimeException e) { // if exception occurs we have to clear the threadlocal
            webInfClassesAnnotationsProcessed = false;
            throw e;
        }
    }

    @Override // called after processAnnotationsXX so using it as hook to reset webInfClassesAnnotationsProcessed
    protected void processAnnotations(final Set<WebXml> fragments, final boolean handlesTypesOnly) {
        webInfClassesAnnotationsProcessed = false;
        super.processAnnotations(fragments, handlesTypesOnly);
    }

    @Override
    protected void processAnnotationsFile(File file, WebXml fragment, boolean handlesTypesOnly) {
        final WebAppInfo webAppInfo = info.get();
        if (webAppInfo == null) {
            super.processAnnotationsFile(file, fragment, handlesTypesOnly);
            return;
        }

        internalProcessAnnotations(file, webAppInfo, fragment, handlesTypesOnly);
    }

    @Override
    protected void processAnnotationsUrl(URL currentUrl, WebXml fragment, boolean handlesTypeOnly) {
        final WebAppInfo webAppInfo = info.get();
        if (webAppInfo == null) {
            super.processAnnotationsUrl(currentUrl, fragment, handlesTypeOnly);
            return;
        }

        File currentUrlAsFile;
        try {
            currentUrlAsFile = URLs.toFile(currentUrl);
        } catch (IllegalArgumentException iae) {
            if ("jndi".equals(currentUrl.getProtocol())) {
                String path = webAppInfo.path;
                if (path.endsWith("/")) {
                    path = path.substring(0, path.length() - 1);
                }

                final String file = currentUrl.getFile();
                String webAppDir = new File(path).getName();
                if ("ROOT".equals(webAppDir)) { // no contextroot so use the host
                    webAppDir = webAppInfo.host;
                    if (webAppDir == null) {
                        webAppDir = "";
                    }
                }
                int idx = file.indexOf(webAppDir);

                // some more tries to manage context config (path can be different from context)
                if (idx < 0) {
                    webAppDir = webAppInfo.moduleId;
                    idx = file.indexOf(webAppDir);
                }
                if (idx < 0) {
                    webAppDir = webAppInfo.contextRoot;
                    idx = file.indexOf(webAppDir);
                }

                if (idx > 0) {
                    String pathUnderWebapp = file.substring(idx + webAppDir.length());
                    if (!pathUnderWebapp.startsWith("/")) {
                        pathUnderWebapp = '/' + pathUnderWebapp;
                    }
                    currentUrlAsFile = new File(path + pathUnderWebapp);
                } else {
                    throw new IllegalArgumentException("can't find path under current webapp deployment [" + webAppInfo.contextRoot + "]");
                }
            } else {
                throw iae;
            }
        }

        internalProcessAnnotations(currentUrlAsFile, webAppInfo, fragment, handlesTypeOnly);
    }

    private void internalProcessAnnotations(final File currentUrlAsFile, final WebAppInfo webAppInfo, final WebXml fragment, final boolean  handlesTypeOnly) {
        for (ClassListInfo webAnnotated : webAppInfo.webAnnotatedClasses) {
            try {
                if (!isIncludedIn(webAnnotated.name, currentUrlAsFile)) {
                    continue;
                }

                internalProcessAnnotationsStream(webAnnotated.list, fragment, handlesTypeOnly);
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException(e);
            }
        }
    }

    private void internalProcessAnnotationsStream(final Collection<String> urls, final WebXml fragment, final boolean handlesTypeOnly) {
        for (String url : urls) {
            InputStream is = null;
            try {
                is = new URL(url).openStream();
                processAnnotationsStream(is, fragment, handlesTypeOnly);
            } catch (MalformedURLException e) {
                throw new IllegalArgumentException(e);
            } catch (IOException e) {
                throw new IllegalArgumentException(e);
            } finally {
                IO.close(is);
            }
        }
    }

    private boolean isIncludedIn(final String filePath, final File classAsFile) throws MalformedURLException {
        final File file = URLs.toFile(new URL(filePath));

        File current = classAsFile;
        boolean webInf = false;
        while (current != null && current.exists()) {
            if (current.equals(file)) {
                final File parent = current.getParentFile();
                if ("classes".equals(current.getName()) && parent != null && "WEB-INF".equals(parent.getName())) {
                    if (webInfClassesAnnotationsProcessed) {
                        return false;
                    }
                    webInfClassesAnnotationsProcessed = true;
                    return true;
                }
                return true;
            }
            if (current.getName().equals("WEB-INF")) {
                webInf = true; // if class loaded from JVM classloader we'll not find it in the war
            }
            current = current.getParentFile();
        }
        return !webInf; // not in the file but not in a war too so use it
    }
}
TOP

Related Classes of org.apache.tomee.catalina.OpenEJBContextConfig

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.