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

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

Go to download

A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle (jaxrs-ri.jar). Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from the command line.

There is a newer version: 3.1.9
Show newest version
/*
 * Copyright (c) 2012, 2021 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.jersey.server;

import java.io.IOException;
import java.io.InputStream;
import java.security.AccessController;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.RuntimeType;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Configurable;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.Feature;

import org.glassfish.jersey.internal.Errors;
import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory;
import org.glassfish.jersey.internal.inject.Binder;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.spi.AutoDiscoverable;
import org.glassfish.jersey.internal.util.Producer;
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.model.internal.ManagedObjectsFinalizer;
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.jersey.uri.UriComponent;


/**
 * The resource configuration for configuring a web application.
 *
 * @author Paul Sandoz
 * @author Martin Matula
 * @author Michal Gajdos
 * @author Marek Potociar
 */
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 transient boolean resetFinders = false;

    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 String applicationName;

        private volatile ClassLoader classLoader = null;

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

            this.resourceFinders = new HashSet<>();

            this.resources = new HashSet<>();
            this.resourcesView = Collections.unmodifiableSet(this.resources);
        }

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

            this.resources = new HashSet<>(original.resources);
            this.resourcesView = Collections.unmodifiableSet(this.resources);

            this.resourceFinders = new HashSet<>(original.resourceFinders);
        }

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

        public void setApplicationName(final String applicationName) {
            this.applicationName = applicationName;
        }

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

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

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

                return builder.build();
            };
        }

        @Override
        public State loadFrom(final Configuration config) {
            super.loadFrom(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 String getApplicationName() {
            return applicationName;
        }
    }

    private static final class ImmutableState extends State {

        private ImmutableState(final State original) {
            super(original);
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        @Override
        public void configureAutoDiscoverableProviders(final InjectionManager injectionManager,
                final Collection autoDiscoverables, final boolean forcedOnly) {
            throw new IllegalStateException(LocalizationMessages.RC_NOT_MODIFIABLE());
        }

        @Override
        public void configureMetaProviders(final InjectionManager injectionManager, final ManagedObjectsFinalizer finalizer) {
            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(final 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(final 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 jakarta.ws.rs.core.Application#getClasses()} * and {@link jakarta.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(final Class applicationClass, final 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(final 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(final Class... classes) { this(Arrays.stream(classes).collect(Collectors.toSet())); } /** * Create a defensive resource configuration copy initialized with a given {@code ResourceConfig}. * * @param original resource configuration to createAndInitialize 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, old values of existing * properties will be replaced by new values. * * @param properties properties to add. * @return updated resource configuration instance. */ public final ResourceConfig addProperties(final 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(final String name, final 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; } /** * 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 InjectionManager} 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 binder} classes is note supported. Binders * must be {@link #registerInstances(Object...) registered as instances}. *

* * @param classes classes to register. * @return updated resource configuration instance. */ public final ResourceConfig registerClasses(final Set> classes) { if (classes == null) { return this; } for (final 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 InjectionManager} 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 binder} classes is note supported. Binders * must be {@link #registerInstances(Object...) registered as instances}. *

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

* Note that registered binders and JAX-RS features are used to initialize and configure * the Jersey runtime {@link InjectionManager} 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(final Set instances) { if (instances == null) { return this; } for (final Object instance : instances) { register(instance); } return this; } /** * Register annotated JAX-RS resource, JAX-RS or Jersey contract provider, JAX-RS feature, * {@link Binder Jersey Binder} instances (singletons) in the {@code ResourceConfig}. *

* Note that registered binders and JAX-RS features are used to initialize and configure * the Jersey runtime {@link InjectionManager} 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(final Object... instances) { if (instances == null) { return this; } return registerInstances(Arrays.stream(instances).collect(Collectors.toSet())); } /** * Register new programmatic resource models in the {@code ResourceConfig}. * * @param resources resource models to register. * @return updated resource configuration instance. */ public final ResourceConfig registerResources(final Resource... resources) { if (resources == null) { return this; } return registerResources(Arrays.stream(resources).collect(Collectors.toSet())); } /** * Register new resource models in the {@code ResourceConfig}. * * @param resources resource models to register. * @return updated resource configuration instance. */ public final ResourceConfig registerResources(final 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(final ResourceFinder resourceFinder) { if (resourceFinder == null) { return this; } invalidateCache(); this.state.registerFinder(resourceFinder); return this; } /** * Set the name of the application. The name is an arbitrary user defined name * which is used to distinguish between Jersey applications in the case that more applications * are deployed on the same runtime (container). The name can be used for example for purposes * of monitoring by JMX when name identifies to which application deployed MBeans belong to. * The name should be unique in the runtime. * * @param applicationName Unique application name. * @return updated resource configuration instance. */ public final ResourceConfig setApplicationName(final String applicationName) { state.setApplicationName(applicationName); 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(final ClassLoader classLoader) { this.state.setClassLoader(classLoader); return this; } /** * Adds array of package names which will be used to scan for components. *

* Package scanning ignores inheritance and therefore {@link Path} annotation * on parent classes and interfaces will be ignored. *

* 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(final String... packages) { return packages(true, packages); } /** * Adds array of package names which will be used to scan for components. *

* Package scanning ignores an inheritance and therefore {@link Path} annotation * on parent classes and interfaces will be ignored. *

* @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(final boolean recursive, final String... packages) { if (packages == null || packages.length == 0) { return this; } return registerFinder(new PackageNamesScanner(packages, recursive)); } /** * Adds array of package names which will be used to scan for components. *

* Package scanning ignores an inheritance and therefore {@link Path} annotation * on parent classes and interfaces will be ignored. *

* @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 classLoader defines the classloader used for scanning the packages and loading the classes. * @param packages array of package names. * @return updated resource configuration instance. * @see #packages(String...) */ public final ResourceConfig packages(final boolean recursive, final ClassLoader classLoader, final String... packages) { if (packages == null || packages.length == 0) { return this; } return registerFinder(new PackageNamesScanner(classLoader, 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(final 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(final boolean recursive, final 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; // Reset ResourceFinders to make sure the next package scanning is successful. if (resetFinders) { for (final ResourceFinder finder : this.state.resourceFinders) { finder.reset(); } resetFinders = false; } } /** * 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. *

* The method also sets the application name from properties if the name is not defined yer * and the property {@link ServerProperties#APPLICATION_NAME} is defined. */ final void lock() { final State current = state; if (!(current instanceof ImmutableState)) { setupApplicationName(); ExternalPropertiesConfigurationFactory.configure(state); state = new ImmutableState(current); } } @Override public final ServerConfig getConfiguration() { return this; } @Override public final Map getProperties() { return state.getProperties(); } @Override public final Object getProperty(final 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 injectionManager injection manager to obtain auto-discoverables from. * @param autoDiscoverables list of registered auto discoverable components. */ final void configureAutoDiscoverableProviders(InjectionManager injectionManager, Collection autoDiscoverables) { state.configureAutoDiscoverableProviders(injectionManager, autoDiscoverables, false); } /** * Configure forced auto-discoverables. * * @param injectionManager injection manager to obtain auto-discoverables from. */ final void configureForcedAutoDiscoverableProviders(InjectionManager injectionManager) { state.configureAutoDiscoverableProviders(injectionManager, Collections.emptyList(), true); } final void configureMetaProviders(InjectionManager injectionManager, ManagedObjectsFinalizer finalizer) { state.configureMetaProviders(injectionManager, finalizer); } @Override public RuntimeType getRuntimeType() { return state.getRuntimeType(); } @Override public boolean isEnabled(final Feature feature) { return state.isEnabled(feature); } @Override public boolean isEnabled(final Class featureClass) { return state.isEnabled(featureClass); } @Override public boolean isRegistered(final Object component) { return state.isRegistered(component); } @Override public boolean isRegistered(final Class componentClass) { return state.isRegistered(componentClass); } @Override public Map, Integer> getContracts(final 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() { final Set> result = new HashSet<>(); final ResourceConfig.State _state = state; final Set rfs = new HashSet<>(_state.getResourceFinders()); // In case new entity is registered the available finders should be reset. resetFinders = true; // classes registered via configuration property final String[] classNames = parsePropertyValue(ServerProperties.PROVIDER_CLASSNAMES); if (classNames != null) { for (final String className : classNames) { try { result.add(_state.getClassLoader().loadClass(className)); } catch (final 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)); } final String[] classPathElements = parsePropertyValue(ServerProperties.PROVIDER_CLASSPATH); if (classPathElements != null) { rfs.add(new FilesScanner(classPathElements, true)); } final AnnotationAcceptingListener parentAfl = AnnotationAcceptingListener.newJaxrsResourceAndProviderListener(_state.getClassLoader()); for (final ResourceFinder resourceFinder : rfs) { AnnotationAcceptingListener afl = parentAfl; if (resourceFinder instanceof PackageNamesScanner) { final ClassLoader classLoader = ((PackageNamesScanner) resourceFinder).getClassloader(); if (!getClassLoader().equals(classLoader)) { afl = AnnotationAcceptingListener.newJaxrsResourceAndProviderListener(classLoader); } } while (resourceFinder.hasNext()) { final String next = resourceFinder.next(); if (afl.accept(next)) { final InputStream in = resourceFinder.open(); try { afl.process(next, in); } catch (final IOException e) { LOGGER.log(Level.WARNING, LocalizationMessages.RESOURCE_CONFIG_UNABLE_TO_PROCESS(next)); } finally { try { in.close(); } catch (final IOException ex) { LOGGER.log(Level.FINER, "Error closing resource stream.", ex); } } } } if (afl != parentAfl) { result.addAll(afl.getAnnotatedClasses()); } } result.addAll(parentAfl.getAnnotatedClasses()); return result; } private String[] parsePropertyValue(final 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() { final Set result = new HashSet<>(); 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(); } /** * Returns encoded value of {@link ApplicationPath} annotation of the Application corresponding * with this ResourceConfig or {@code null} when the annotation is not present. * * @return Returns encoded value of {@link ApplicationPath} annotation of the Application * corresponding with this ResourceConfig. */ public final String getApplicationPath() { final Application application; if (ResourceConfig.class.isInstance(_getApplication())) { final Application unwrap = unwrapCustomRootApplication((ResourceConfig) _getApplication()); application = unwrap != null ? unwrap : _getApplication(); } else { application = _getApplication(); } final ApplicationPath appPath = application.getClass().getAnnotation(ApplicationPath.class); final String value; if (appPath != null && !appPath.value().isEmpty() && !appPath.value().trim().equals("/")) { final String val = appPath.value().trim(); value = UriComponent.encode(val.startsWith("/") ? val.substring(1) : val, UriComponent.Type.PATH); } else { value = null; } return value; } /** * Allows overriding the {@link #getApplication()} method functionality in {@link WrappingResourceConfig}. * * @return JAX-RS application corresponding with this ResourceConfig. */ Application _getApplication() { return this; } /** * Get the name of the Jersey application. * * @return Name of the application. * @see #setApplicationName(String) */ public String getApplicationName() { return state.getApplicationName(); } /** * Method used by ApplicationHandler to retrieve application class * (this method is overridden by {@link 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(final 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(final Application app) { throw new UnsupportedOperationException(); } private static class WrappingResourceConfig extends ResourceConfig { private Application application; private Class applicationClass; private final Set> defaultClasses = new HashSet<>(); public WrappingResourceConfig( final Application application, final Class applicationClass, final 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 jakarta.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(final 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. final 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()); super.setApplicationName(rc.getApplicationName()); super.setClassLoader(rc.getClassLoader()); rc.lock(); } else if (application != null) { super.addProperties(application.getProperties()); } } @Override Set> _getClasses() { final Set> result = new HashSet<>(); final 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(final 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(final ResourceConfig original) { super(original); this.application = original; final Application customRootApp = ResourceConfig.unwrapCustomRootApplication(original); final Set rootSingletons = customRootApp != null ? registerComponentsOf(customRootApp) : null; originalRegistrations = Collections.newSetFromMap(new IdentityHashMap<>()); originalRegistrations.addAll(super.getRegisteredClasses()); // Do not call the same Application#getSingletons twice final Set origSingletons = customRootApp != null ? rootSingletons : original.getSingletons(); // Register externally provided instances. final Set externalInstances = origSingletons.stream() .filter(external -> !originalRegistrations.contains(external.getClass())) .collect(Collectors.toSet()); registerInstances(externalInstances); // Register externally provided classes. final Set> externalClasses = original.getClasses().stream() .filter(external -> !originalRegistrations.contains(external)) .collect(Collectors.toSet()); registerClasses(externalClasses); } private Set registerComponentsOf(final Application application) { return Errors.process(new Producer>() { @Override public Set call() { // First register instances that should take precedence over classes // in case of duplicate registrations final Set singletons = application.getSingletons(); if (singletons != null) { registerInstances( singletons.stream() .filter(input -> { if (input == null) { Errors.warning(application, LocalizationMessages.NON_INSTANTIABLE_COMPONENT(null)); } return input != null; }) .collect(Collectors.toSet())); } final Set> classes = application.getClasses(); if (classes != null) { registerClasses(classes.stream() .filter(input -> { if (input == null) { Errors.warning(application, LocalizationMessages.NON_INSTANTIABLE_COMPONENT(null)); } return input != null; }) .collect(Collectors.toSet())); } return singletons; } }); } private RuntimeConfig(final Application application) { super(); this.application = application; if (application != null) { registerComponentsOf(application); // Copy all available properties. addProperties(application.getProperties()); } 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 application - return null return null; } else if (app instanceof ResourceConfig) { resourceConfig = (ResourceConfig) app; } else { break; } } return app; } /** * Get the most internal wrapped {@link Application application} class. *

* This method is similar to {@link ResourceConfig#getApplication()} except if provided application was * created by wrapping multiple {@code ResourceConfig} instances, this method will return the original (inner-most) * JAX-RS {@code Application} sub-class rather than a potentially intermediate {@code ResourceConfig} wrapper. *

* * @param application application that is potentially wrapped. * @return the original, inner-most {@link Application} subclass. May return the same instance directly, * in case the supplied {@code application} instance is not a wrapper {@code ResourceConfig} instance. */ static Application unwrapApplication(Application application) { while (application instanceof ResourceConfig) { final Application wrappedApplication = ((ResourceConfig) application).getApplication(); if (wrappedApplication == application) { break; } application = wrappedApplication; } return application; } private void setupApplicationName() { final String appName = ServerProperties.getValue(getProperties(), ServerProperties.APPLICATION_NAME, null, String.class); if (appName != null && getApplicationName() == null) { setApplicationName(appName); } } }