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

org.glassfish.jersey.server.ResourceConfig Maven / Gradle / Ivy

There is a newer version: 2.0-rc1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2012-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
 * http://glassfish.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.jersey.server;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.RuntimeType;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Configurable;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Feature;

import org.glassfish.jersey.internal.Errors;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.Tokenizer;
import org.glassfish.jersey.model.ContractProvider;
import org.glassfish.jersey.model.internal.CommonConfig;
import org.glassfish.jersey.model.internal.ComponentBag;
import org.glassfish.jersey.process.Inflector;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.jersey.server.internal.scanning.AnnotationAcceptingListener;
import org.glassfish.jersey.server.internal.scanning.FilesScanner;
import org.glassfish.jersey.server.internal.scanning.PackageNamesScanner;
import org.glassfish.jersey.server.model.Resource;

import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.Binder;

import com.google.common.base.Predicate;
import com.google.common.collect.Sets;

/**
 * The resource configuration for configuring a web application.
 *
 * @author Paul Sandoz
 * @author Martin Matula (martin.matula at oracle.com)
 * @author Michal Gajdos (michal.gajdos at oracle.com)
 * @author Marek Potociar (marek.potociar at oracle.com)
 */
public class ResourceConfig extends Application implements Configurable, ServerConfig {

    private static final Logger LOGGER = Logger.getLogger(ResourceConfig.class.getName());

    private transient Set> cachedClasses = null;
    private transient Set> cachedClassesView = null;
    private transient Set cachedSingletons = null;
    private transient Set cachedSingletonsView = null;

    private volatile State state;

    private static class State extends CommonConfig implements ServerConfig {

        private final Set resourceFinders;

        private final Set resources;
        private final Set resourcesView;

        private volatile ClassLoader classLoader = null;

        public State() {
            super(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL);
            this.classLoader = ReflectionHelper.getContextClassLoader();

            this.resourceFinders = Sets.newHashSet();

            this.resources = Sets.newHashSet();
            this.resourcesView = Collections.unmodifiableSet(this.resources);
        }

        public State(State original) {
            super(original);
            this.classLoader = original.classLoader;

            this.resources = Sets.newHashSet(original.resources);
            this.resourcesView = Collections.unmodifiableSet(this.resources);

            this.resourceFinders = Sets.newHashSet(original.resourceFinders);
        }

        public void setClassLoader(ClassLoader classLoader) {
            this.classLoader = classLoader;
        }

        public void registerResources(Set resources) {
            this.resources.addAll(resources);
        }

        public void registerFinder(ResourceFinder resourceFinder) {
            this.resourceFinders.add(resourceFinder);
        }

        @Override
        protected Inflector getModelEnhancer(final Class componentClass) {
            return new Inflector() {
                @Override
                public ContractProvider apply(ContractProvider.Builder builder) {
                    if (builder.getScope() == null && builder.getContracts().isEmpty() &&
                            Resource.getPath(componentClass) != null) {
                        builder.scope(RequestScoped.class);
                    }

                    return builder.build();
                }
            };

        }

        @Override
        public State replaceWith(Configuration config) {
            super.replaceWith(config);
            this.resourceFinders.clear();
            this.resources.clear();

            State other = null;
            if (config instanceof ResourceConfig) {
                other = ((ResourceConfig) config).state;
            }
            if (config instanceof State) {
                other = (State) config;
            }

            if (other != null) {
                this.resourceFinders.addAll(other.resourceFinders);
                this.resources.addAll(other.resources);
            }

            return this;
        }

        @Override
        public final Set getResources() {
            return resourcesView;
        }

        @Override
        public ServerConfig getConfiguration() {
            return this;
        }

        /**
         * Get the registered resource finders.
         *
         * @return registered resource finders.
         */
        public Set getResourceFinders() {
            return resourceFinders;
        }

        /**
         * Get resource and provider class loader.
         *
         * @return class loader to be used when looking up the resource classes and providers.
         */
        public ClassLoader getClassLoader() {
            return classLoader;
        }
    }

    private static final class ImmutableState extends State {
        private ImmutableState(State original) {
            super(original);
        }

        @Override
        public void setClassLoader(ClassLoader classLoader) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public void registerResources(Set resources) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public void registerFinder(ResourceFinder resourceFinder) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State addProperties(Map properties) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State property(String name, Object value) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Class componentClass) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Class componentClass, int bindingPriority) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Class componentClass, Class... contracts) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Class componentClass, Map, Integer> contracts) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Object component) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Object component, int bindingPriority) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Object component, Class... contracts) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State register(Object component, Map, Integer> contracts) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public State setProperties(Map properties) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public void configureAutoDiscoverableProviders(final ServiceLocator locator) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public void configureMetaProviders(ServiceLocator locator) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }
    }

    /**
     * Returns a {@code ResourceConfig} instance for the supplied application.
     *
     * If the application is an instance of {@code ResourceConfig} the method returns defensive copy of the resource config.
     * Otherwise it creates a new {@code ResourceConfig} from the application.
     *
     * @param application Application to provide the {@code ResourceConfig} instance for.
     * @return ResourceConfig instance for the supplied application.
     */
    public static ResourceConfig forApplication(Application application) {
        return (application instanceof ResourceConfig) ? ((ResourceConfig) application) : new WrappingResourceConfig
                (application, null, null);
    }

    /**
     * Returns a {@code ResourceConfig} instance wrapping the application of the supplied class.
     *
     * @param applicationClass Class representing a JAX-RS application.
     * @return ResourceConfig wrapping the JAX-RS application defined by the supplied class.
     */
    public static ResourceConfig forApplicationClass(Class applicationClass) {
        return new WrappingResourceConfig(null, applicationClass, null);
    }

    /**
     * Returns a {@code ResourceConfig} instance wrapping the application of the supplied class.
     *
     * This method provides an option of supplying the set of classes that should be returned from {@link #getClasses()}
     * method if the application defined by the supplied application class returns empty sets from {@link javax.ws.rs.core
     * .Application#getClasses()}
     * and {@link javax.ws.rs.core.Application#getSingletons()} methods.
     *
     * @param applicationClass Class representing a JAX-RS application.
     * @param defaultClasses   Default set of classes that should be returned from {@link #getClasses()} if the underlying
     *                         application does not provide any classes and singletons.
     * @return ResourceConfig wrapping the JAX-RS application defined by the supplied class.
     */
    public static ResourceConfig forApplicationClass(Class applicationClass,
                                                     Set> defaultClasses) {
        return new WrappingResourceConfig(null, applicationClass, defaultClasses);
    }

    /**
     * Create a new resource configuration without any custom properties or
     * resource and provider classes.
     */
    public ResourceConfig() {
        this.state = new State();
    }

    /**
     * Create a new resource configuration initialized with a given set of
     * resource/provider classes.
     *
     * @param classes application-specific resource and/or provider classes.
     */
    public ResourceConfig(Set> classes) {
        this();
        this.registerClasses(classes);
    }

    /**
     * Create a new resource configuration initialized with a given set of
     * resource/provider classes.
     *
     * @param classes application-specific resource and/or provider classes.
     */
    public ResourceConfig(Class... classes) {
        this(Sets.newHashSet(classes));
    }

    /**
     * Create a defensive resource configuration copy initialized with a given {@code ResourceConfig}.
     *
     * @param original resource configuration to create a defensive copy from.
     */
    public ResourceConfig(final ResourceConfig original) {
        this.state = new State(original.state);
    }

    /**
     * Add properties to {@code ResourceConfig}.
     *
     * If any of the added properties exists already, he values of the existing
     * properties will be replaced with new values.
     *
     * @param properties properties to add.
     * @return updated resource configuration instance.
     */
    public final ResourceConfig addProperties(Map properties) {
        state.addProperties(properties);
        return this;
    }

    /**
     * Set new configuration properties replacing all previously set properties.
     *
     * @param properties new set of configuration properties. The content of
     *                   the map will replace any existing properties set on the configuration
     *                   instance.
     * @return the updated configuration instance.
     */
    public ResourceConfig setProperties(final Map properties) {
        state.setProperties(properties);
        return this;
    }

    @Override
    public ResourceConfig property(String name, Object value) {
        state.property(name, value);
        return this;
    }

    @Override
    public ResourceConfig register(final Class componentClass) {
        invalidateCache();
        state.register(componentClass);
        return this;
    }

    @Override
    public ResourceConfig register(final Class componentClass, final int bindingPriority) {
        invalidateCache();
        state.register(componentClass, bindingPriority);
        return this;
    }

    @Override
    public ResourceConfig register(final Class componentClass, final Class... contracts) {
        invalidateCache();
        state.register(componentClass, contracts);
        return this;
    }

    @Override
    public ResourceConfig register(final Class componentClass, final Map, Integer> contracts) {
        invalidateCache();
        state.register(componentClass, contracts);
        return this;
    }

    @Override
    public ResourceConfig register(final Object component) {
        invalidateCache();
        state.register(component);
        return this;
    }

    @Override
    public ResourceConfig register(final Object component, final int bindingPriority) {
        invalidateCache();
        state.register(component, bindingPriority);
        return this;
    }

    @Override
    public ResourceConfig register(final Object component, final Class... contracts) {
        invalidateCache();
        state.register(component, contracts);
        return this;
    }

    @Override
    public ResourceConfig register(final Object component, final Map, Integer> contracts) {
        invalidateCache();
        state.register(component, contracts);
        return this;
    }

    @Override
    public ResourceConfig replaceWith(Configuration config) {
        invalidateCache();
        state.replaceWith(config);
        return this;
    }

    /**
     * Register annotated JAX-RS resource, JAX-RS or Jersey contract provider or JAX-RS feature
     * in the {@code ResourceConfig}.
     * 

* Note that registered JAX-RS features are used to initialize and configure * the Jersey runtime {@link ServiceLocator} instance during application deployment, but are * otherwise ignored by server-side runtime, unless they implement also another contract * recognized by Jersey runtime. *

*

* Also note that registration of {@link Binder HK2 binder} classes is note supported. HK2 binders * must be {@link #registerInstances(Object...) registered as instances}. *

* * @param classes classes to register. * @return updated resource configuration instance. */ public final ResourceConfig registerClasses(Set> classes) { if (classes == null) { return this; } for (Class cls : classes) { register(cls); } return this; } /** * Register annotated JAX-RS resource, JAX-RS or Jersey contract provider or JAX-RS feature * in the {@code ResourceConfig}. *

* Note that registered JAX-RS features are used to initialize and configure * the Jersey runtime {@link ServiceLocator} instance during application deployment, but are * otherwise ignored by server-side runtime, unless they implement also another contract * recognized by Jersey runtime. *

*

* Also note that registration of {@link Binder HK2 binder} classes is note supported. HK2 binders * must be {@link #registerInstances(Object...) registered as instances}. *

* * @param classes classes to register. * @return updated resource configuration instance. */ public final ResourceConfig registerClasses(Class... classes) { if (classes == null) { return this; } return registerClasses(Sets.newHashSet(classes)); } /** * Register annotated JAX-RS resource, JAX-RS or Jersey contract provider, JAX-RS feature * or {@link Binder HK2 binder} instances (singletons) in the {@code ResourceConfig}. *

* Note that registered HK2 binders and JAX-RS features are used to initialize and configure * the Jersey runtime {@link ServiceLocator} instance during application deployment, but are * otherwise ignored by server-side runtime, unless they implement also another contract * recognized by Jersey runtime. *

* * @param instances instances to register. * @return updated resource configuration instance. */ public final ResourceConfig registerInstances(Set instances) { if (instances == null) { return this; } for (Object instance : instances) { register(instance); } return this; } /** * Register annotated JAX-RS resource, JAX-RS or Jersey contract provider, JAX-RS feature * or {@link Binder HK2 binder} instances (singletons) in the {@code ResourceConfig}. *

* Note that registered HK2 binders and JAX-RS features are used to initialize and configure * the Jersey runtime {@link ServiceLocator} instance during application deployment, but are * otherwise ignored by server-side runtime, unless they implement also another contract * recognized by Jersey runtime. *

* * @param instances instances to register. * @return updated resource configuration instance. */ public final ResourceConfig registerInstances(Object... instances) { if (instances == null) { return this; } return registerInstances(Sets.newHashSet(instances)); } /** * Register new programmatic resource models in the {@code ResourceConfig}. * * @param resources resource models to register. * @return updated resource configuration instance. */ public final ResourceConfig registerResources(Resource... resources) { if (resources == null) { return this; } return registerResources(Sets.newHashSet(resources)); } /** * Register new resource models in the {@code ResourceConfig}. * * @param resources resource models to register. * @return updated resource configuration instance. */ public final ResourceConfig registerResources(Set resources) { if (resources == null) { return this; } this.state.registerResources(resources); return this; } /** * Add a {@link ResourceFinder} to {@code ResourceConfig}. * * @param resourceFinder {@link ResourceFinder} * @return updated resource configuration instance. */ public final ResourceConfig registerFinder(ResourceFinder resourceFinder) { if (resourceFinder == null) { return this; } this.state.registerFinder(resourceFinder); return this; } /** * Set {@link ClassLoader} which will be used for resource discovery. * * @param classLoader provided {@link ClassLoader}. * @return updated resource configuration instance. */ public final ResourceConfig setClassLoader(ClassLoader classLoader) { this.state.setClassLoader(classLoader); return this; } /** * Adds array of package names which will be used to scan for components. * * Packages will be scanned recursively, including all nested packages. * * @param packages array of package names. * @return updated resource configuration instance. * @see #packages(boolean, String...) */ public final ResourceConfig packages(String... packages) { return packages(true, packages); } /** * Adds array of package names which will be used to scan for components. * * @param recursive defines whether any nested packages in the collection of specified * package names should be recursively scanned (value of {@code true}) * as part of the package scanning or not (value of {@code false}). * @param packages array of package names. * @return updated resource configuration instance. * @see #packages(String...) */ public final ResourceConfig packages(boolean recursive, String... packages) { if (packages == null || packages.length == 0) { return this; } return registerFinder(new PackageNamesScanner(packages, recursive)); } /** * Adds array of file and directory names to scan for components. * * Any directories in the list will be scanned recursively, including their sub-directories. * * @param files array of file and directory names. * @return updated resource configuration instance. */ public final ResourceConfig files(String... files) { return files(true, files); } /** * Adds array of file and directory names to scan for components. * * @param recursive defines whether any sub-directories of the directories specified * in the collection of file names should be recursively scanned (value of {@code true}) * as part of the file scanning or not (value of {@code false}). * @param files array of file and directory names. * @return updated resource configuration instance. */ public final ResourceConfig files(boolean recursive, String... files) { if (files == null || files.length == 0) { return this; } return registerFinder(new FilesScanner(files, recursive)); } /** * Invalidate cached component instances and classes. */ final void invalidateCache() { this.cachedClasses = null; this.cachedClassesView = null; this.cachedSingletons = null; this.cachedSingletonsView = null; } /** * Switches the ResourceConfig to read-only state. * * Called by the WrappingResourceConfig if this ResourceConfig is set as the application. * Also called by ApplicationHandler on WrappingResourceConfig at the point when it is going * to build the resource model. */ final void lock() { final State current = state; if (!(current instanceof ImmutableState)) { state = new ImmutableState(current); } } @Override public final ServerConfig getConfiguration() { return this; } @Override public final Map getProperties() { return state.getProperties(); } @Override public final Object getProperty(String name) { return state.getProperty(name); } @Override public Collection getPropertyNames() { return state.getPropertyNames(); } @Override public final boolean isProperty(final String name) { return state.isProperty(name); } @Override public final Set> getClasses() { if (cachedClassesView == null) { cachedClasses = _getClasses(); cachedClassesView = Collections.unmodifiableSet(cachedClasses); } return cachedClassesView; } @Override public final Set getInstances() { return getSingletons(); } @Override public final Set getSingletons() { if (cachedSingletonsView == null) { cachedSingletons = _getSingletons(); cachedSingletonsView = Collections.unmodifiableSet(cachedSingletons == null ? new HashSet() : cachedSingletons); } return cachedSingletonsView; } /** * Get the internal component bag. * * @return internal component bag. */ final ComponentBag getComponentBag() { return state.getComponentBag(); } /** * Configure auto-discoverables. * * @param locator service locator to obtain auto-discoverables from. */ final void configureAutoDiscoverableProviders(final ServiceLocator locator) { state.configureAutoDiscoverableProviders(locator); } /** * Configure custom binders registered in the resource config. * * @param locator service locator to update with the custom binders. */ final void configureMetaProviders(final ServiceLocator locator) { state.configureMetaProviders(locator); } @Override public RuntimeType getRuntimeType() { return state.getRuntimeType(); } @Override public boolean isEnabled(Feature feature) { return state.isEnabled(feature); } @Override public boolean isEnabled(Class featureClass) { return state.isEnabled(featureClass); } @Override public boolean isRegistered(Object component) { return state.isRegistered(component); } @Override public boolean isRegistered(Class componentClass) { return state.isRegistered(componentClass); } @Override public Map, Integer> getContracts(Class componentClass) { return state.getContracts(componentClass); } /** * Get configured resource and/or provider classes. The method is overridden * in a {@link WrappingResourceConfig private sub-type}. * * @return set of configured resource and/or provider classes. */ Set> _getClasses() { final Set> result = scanClasses(); result.addAll(state.getClasses()); return result; } private Set> scanClasses() { Set> result = Sets.newHashSet(); final ResourceConfig.State _state = state; Set rfs = Sets.newHashSet(_state.getResourceFinders()); // classes registered via configuration property String[] classNames = parsePropertyValue(ServerProperties.PROVIDER_CLASSNAMES); if (classNames != null) { for (String className : classNames) { try { result.add(_state.getClassLoader().loadClass(className)); } catch (ClassNotFoundException e) { LOGGER.log(Level.CONFIG, LocalizationMessages.UNABLE_TO_LOAD_CLASS(className)); } } } final String[] packageNames = parsePropertyValue(ServerProperties.PROVIDER_PACKAGES); if (packageNames != null) { final Object p = getProperty(ServerProperties.PROVIDER_SCANNING_RECURSIVE); final boolean recursive = p == null || PropertiesHelper.isProperty(p); rfs.add(new PackageNamesScanner(packageNames, recursive)); } String[] classPathElements = parsePropertyValue(ServerProperties.PROVIDER_CLASSPATH); if (classPathElements != null) { rfs.add(new FilesScanner(classPathElements, true)); } AnnotationAcceptingListener afl = AnnotationAcceptingListener.newJaxrsResourceAndProviderListener(_state.getClassLoader()); for (ResourceFinder resourceFinder : rfs) { while (resourceFinder.hasNext()) { final String next = resourceFinder.next(); if (afl.accept(next)) { try { afl.process(next, resourceFinder.open()); } catch (IOException e) { // TODO L10N LOGGER.log(Level.WARNING, "Unable to process {0}", next); } } } } result.addAll(afl.getAnnotatedClasses()); return result; } private String[] parsePropertyValue(String propertyName) { String[] classNames = null; final Object o = state.getProperties().get(propertyName); if (o != null) { if (o instanceof String) { classNames = Tokenizer.tokenize((String) o); } else if (o instanceof String[]) { classNames = Tokenizer.tokenize((String[]) o); } } return classNames; } /** * Return classes which were registered by the user and not found by class path scanning (or any other scanning). * * @return Set of classes registered by the user. */ Set> getRegisteredClasses() { return state.getComponentBag().getRegistrations(); } /** * Get configured resource and/or provider instances. The method is overridden * in a {@link WrappingResourceConfig private sub-type}. * * @return set of configured resource and/or provider instances. */ Set _getSingletons() { Set result = Sets.newHashSet(); result.addAll(state.getInstances()); return result; } @Override public final Set getResources() { return state.getResources(); } /** * Get resource and provider class loader. * * @return class loader to be used when looking up the resource classes and * providers. */ public final ClassLoader getClassLoader() { return state.getClassLoader(); } /** * Returns JAX-RS application corresponding with this ResourceConfig. * * @return JAX-RS application corresponding with this ResourceConfig. */ public final Application getApplication() { return _getApplication(); } /** * Allows overriding the {@link #getApplication()} method functionality in {@link WrappingResourceConfig}. * * @return JAX-RS application corresponding with this ResourceConfig. */ Application _getApplication() { return this; } /** * Gets the most internal wrapped {@link Application application} class. This method is similar to {@link #getApplication()} * except ff this config was created by wrapping multiple resource configs this method returns the original application * and not the wrapped resource config. * * @return Application. */ final Application getWrappedApplication() { Application app = this; while (app instanceof ResourceConfig) { final Application wrappedApplication = ((ResourceConfig) app).getApplication(); if (wrappedApplication == app) { break; } app = wrappedApplication; } return app; } /** * Method used by ApplicationHandler to retrieve application class * (this method is overridden by WrappingResourceConfig). * * @return application class */ Class getApplicationClass() { return null; } /** * This method is used by ApplicationHandler to set application instance to the resource config (should * always be called on WrappingResourceConfig instance, never on plain instances of ResourceConfig * unless we have a bug in the code). * * @param app JAX-RS application * @return this ResourceConfig instance (for convenience) */ final ResourceConfig setApplication(Application app) { return _setApplication(app); } /** * Allows overriding the setApplication() method functionality in WrappingResourceConfig. * * @param app application to be set for this ResourceConfig * @return this resource config instance */ ResourceConfig _setApplication(Application app) { throw new UnsupportedOperationException(); } private static class WrappingResourceConfig extends ResourceConfig { private Application application; private Class applicationClass; private final Set> defaultClasses = Sets.newHashSet(); public WrappingResourceConfig( Application application, Class applicationClass, Set> defaultClasses) { if (application == null && applicationClass == null) { throw new IllegalArgumentException(LocalizationMessages.RESOURCE_CONFIG_ERROR_NULL_APPLICATIONCLASS()); } this.application = application; this.applicationClass = applicationClass; if (defaultClasses != null) { this.defaultClasses.addAll(defaultClasses); } mergeApplications(application); } /** * Set the {@link javax.ws.rs.core.Application JAX-RS Application instance} * in the {@code ResourceConfig}. * * This method is used by the {@link org.glassfish.jersey.server.ApplicationHandler} in case this resource * configuration instance was created with application class rather than application instance. * * @param application JAX-RS Application instance. * @return updated resource configuration instance. */ @Override ResourceConfig _setApplication(Application application) { this.application = application; this.applicationClass = null; mergeApplications(application); return this; } /** * Get the original underlying JAX-RS {@link Application} instance used to * initialize the resource configuration instance. * * @return JAX-RS application instance. */ @Override Application _getApplication() { return application; } /** * Get the original JAX-RS {@link Application} class provided it was not * instantiated yet. A {@code null} is returned in case the class has been * instantiated already or was not configured at all. *

* This class will be used to initialize the resource configuration instance. * If there is no JAX-RS application class set, or if the class has been * instantiated already, the method will return {@code null}. *

* * @return original JAX-RS application class or {@code null} if there is no * such class configured or if the class has been already instantiated. */ @Override Class getApplicationClass() { return applicationClass; } /** * Merges fields (e.g. custom binders, properties) of the given application with this application. *

* The merging should be done because of the possibility of reloading this {@code ResourceConfig} in a container * so this resource config should know about custom binders and properties of the underlying application to ensure * the reload process will complete successfully. *

* * @param application the application which fields should be merged with this application. * @see org.glassfish.jersey.server.spi.Container#reload() * @see org.glassfish.jersey.server.spi.Container#reload(ResourceConfig) */ private void mergeApplications(final Application application) { if (application instanceof ResourceConfig) { // Merge custom binders. ResourceConfig rc = (ResourceConfig) application; // Merge resources super.registerResources(rc.getResources()); // properties set on the wrapping resource config take precedence // (as those are retrieved from the web.xml, for example) rc.invalidateCache(); rc.addProperties(super.getProperties()); super.addProperties(rc.getProperties()); rc.lock(); } else if (application != null) { super.addProperties(application.getProperties()); } } @Override Set> _getClasses() { Set> result = Sets.newHashSet(); Set> applicationClasses = application.getClasses(); result.addAll(applicationClasses == null ? new HashSet>() : applicationClasses); if (result.isEmpty() && getSingletons().isEmpty()) { result.addAll(defaultClasses); } // if the application is not an instance of ResourceConfig, handle scanning triggered via properties if (!(application instanceof ResourceConfig)) { result.addAll(super._getClasses()); } return result; } @Override Set _getSingletons() { return application.getSingletons(); } } /** * Create runtime configuration initialized from a given deploy-time JAX-RS/Jersey * application configuration. * * @param application deploy-time JAX-RS/Jersey application configuration. * @return initialized run-time resource config. */ static ResourceConfig createRuntimeConfig(Application application) { return (application instanceof ResourceConfig) ? new RuntimeConfig((ResourceConfig) application) : new RuntimeConfig(application); } private static class RuntimeConfig extends ResourceConfig { private final Set> originalRegistrations; private final Application application; private RuntimeConfig(ResourceConfig original) { super(original); this.application = original; Application customRootApp = ResourceConfig.unwrapCustomRootApplication(original); if (customRootApp != null) { registerComponentsOf(customRootApp); } originalRegistrations = Sets.newIdentityHashSet(); originalRegistrations.addAll(super.getRegisteredClasses()); // Register externally provided instances. Set externalInstances = Sets.filter(original.getSingletons(), new Predicate() { @Override public boolean apply(Object external) { return !originalRegistrations.contains(external.getClass()); } }); registerInstances(externalInstances); // Register externally provided classes. Set> externalClasses = Sets.filter(original.getClasses(), new Predicate>() { @Override public boolean apply(Class external) { return !originalRegistrations.contains(external); } }); registerClasses(externalClasses); } private void registerComponentsOf(final Application application) { Errors.processWithException(new Runnable() { @Override public void run() { // First register instances that should take precedence over classes // in case of duplicate registrations final Set singletons = application.getSingletons(); if (singletons != null) { registerInstances(Sets.filter(singletons, new Predicate() { @Override public boolean apply(Object input) { if (input == null) { Errors.warning(application, LocalizationMessages.NON_INSTANTIABLE_COMPONENT(input)); } return input != null; } })); } final Set> classes = application.getClasses(); if (classes != null) { registerClasses(Sets.filter(classes, new Predicate>() { @Override public boolean apply(Class input) { if (input == null) { Errors.warning(application, LocalizationMessages.NON_INSTANTIABLE_COMPONENT(input)); } return input != null; } })); } } }); } private RuntimeConfig(Application application) { super(); this.application = application; if (application != null) { registerComponentsOf(application); } originalRegistrations = super.getRegisteredClasses(); } @Override Set> _getClasses() { // Get only a read-only classes cached in internal state. return super.state.getClasses(); } @Override Set _getSingletons() { // Get only a read-only classes cached in internal state. return super.state.getInstances(); } @Override Set> getRegisteredClasses() { return originalRegistrations; } @Override Application _getApplication() { return application; } } private static Application unwrapCustomRootApplication(ResourceConfig resourceConfig) { Application app = null; while (resourceConfig != null) { app = resourceConfig.getApplication(); if (app == resourceConfig) { // resource config is the root app - return null return null; } else if (app instanceof ResourceConfig) { resourceConfig = (ResourceConfig) app; } else { break; } } return app; } }