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

org.glassfish.jersey.internal.inject.Providers 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.8
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.internal.inject;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.core.Feature;

import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.model.ContractProvider;
import org.glassfish.jersey.model.internal.RankedComparator;
import org.glassfish.jersey.model.internal.RankedProvider;
import org.glassfish.jersey.spi.Contract;

import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * Utility class providing a set of utility methods for easier and more type-safe
 * interaction with HK2 injection layer.
 *
 * @author Marek Potociar (marek.potociar at oracle.com)
 * @author Miroslav Fuksa (miroslav.fuksa at oracle.com)
 */
public class Providers {
    private static final Logger LOGGER = Logger.getLogger(Providers.class.getName());

    /**
     * Map of all standard JAX-RS providers and their run-time affinity.
     */
    private static final Map, ProviderRuntime> JAX_RS_PROVIDER_INTERFACE_WHITELIST =
            getJaxRsProviderInterfaces();

    private static Map, ProviderRuntime> getJaxRsProviderInterfaces() {
        Map, ProviderRuntime> interfaces = new HashMap, ProviderRuntime>();

        interfaces.put(javax.ws.rs.ext.ContextResolver.class, ProviderRuntime.BOTH);
        interfaces.put(javax.ws.rs.ext.ExceptionMapper.class, ProviderRuntime.BOTH);
        interfaces.put(javax.ws.rs.ext.MessageBodyReader.class, ProviderRuntime.BOTH);
        interfaces.put(javax.ws.rs.ext.MessageBodyWriter.class, ProviderRuntime.BOTH);
        interfaces.put(javax.ws.rs.ext.ReaderInterceptor.class, ProviderRuntime.BOTH);
        interfaces.put(javax.ws.rs.ext.WriterInterceptor.class, ProviderRuntime.BOTH);
        interfaces.put(javax.ws.rs.ext.ParamConverterProvider.class, ProviderRuntime.BOTH);

        interfaces.put(javax.ws.rs.container.ContainerRequestFilter.class, ProviderRuntime.SERVER);
        interfaces.put(javax.ws.rs.container.ContainerResponseFilter.class, ProviderRuntime.SERVER);
        interfaces.put(javax.ws.rs.container.DynamicFeature.class, ProviderRuntime.SERVER);

        interfaces.put(javax.ws.rs.client.ClientResponseFilter.class, ProviderRuntime.CLIENT);
        interfaces.put(javax.ws.rs.client.ClientRequestFilter.class, ProviderRuntime.CLIENT);

        return interfaces;
    }

    /**
     * Map of all supported external (i.e. non-Jersey) contracts and their run-time affinity.
     */
    private static final Map, ProviderRuntime> EXTERNAL_PROVIDER_INTERFACE_WHITELIST =
            getExternalProviderInterfaces();

    private static Map, ProviderRuntime> getExternalProviderInterfaces() {
        Map, ProviderRuntime> interfaces = new HashMap, ProviderRuntime>();

        // JAX-RS
        interfaces.putAll(JAX_RS_PROVIDER_INTERFACE_WHITELIST);
        interfaces.put(javax.ws.rs.core.Feature.class, ProviderRuntime.BOTH);

        // HK2
        interfaces.put(org.glassfish.hk2.utilities.Binder.class, ProviderRuntime.BOTH);

        return interfaces;
    }

    private enum ProviderRuntime {

        BOTH(null), SERVER(RuntimeType.SERVER), CLIENT(RuntimeType.CLIENT);

        private final RuntimeType runtime;

        private ProviderRuntime(RuntimeType runtime) {
            this.runtime = runtime;
        }

        public RuntimeType getRuntime() {
            return runtime;
        }
    }

    private Providers() {
    }

    /**
     * Wrap an instance into a HK2 service factory.
     *
     * @param       Java type if the contract produced by the provider and factory.
     * @param instance instance to be wrapped into (and provided by) the factory.
     * @return HK2 service factory wrapping and providing the instance.
     */
    public static  Factory factoryOf(final T instance) {
        return new Factory() {

            @Override
            public T provide() {
                return instance;
            }

            @Override
            public void dispose(T instance) {
                //not used
            }
        };
    }

    /**
     * Get the set of default providers registered for the given service provider contract
     * in the underlying {@link ServiceLocator HK2 service locator} container.
     *
     * @param       service provider contract Java type.
     * @param locator  underlying HK2 service locator.
     * @param contract service provider contract.
     * @return set of all available default service provider instances for the contract.
     */
    public static  Set getProviders(ServiceLocator locator, Class contract) {
        final Collection> hk2Providers = getAllServiceHandles(locator, contract);
        return getClasses(hk2Providers);
    }

    /**
     * Get the set of all custom providers registered for the given service provider contract
     * in the underlying {@link ServiceLocator HK2 service locator} container.
     *
     * @param       service provider contract Java type.
     * @param locator  underlying HK2 service locator.
     * @param contract service provider contract.
     * @return set of all available service provider instances for the contract.
     */
    public static  Set getCustomProviders(ServiceLocator locator, Class contract) {
        final Collection> hk2Providers = getAllServiceHandles(locator, contract, new CustomAnnotationImpl());
        return getClasses(hk2Providers);
    }

    /**
     * Get the iterable of all providers (custom and default) registered for the given service provider contract
     * in the underlying {@link ServiceLocator HK2 service locator} container.
     *
     * @param       service provider contract Java type.
     * @param locator  underlying HK2 service locator.
     * @param contract service provider contract.
     * @return iterable of all available service provider instances for the contract. Return value is never null.
     */
    public static  Iterable getAllProviders(ServiceLocator locator, Class contract) {
        return getAllProviders(locator, contract, (Comparator) null);
    }

    /**
     * Get the iterable of all {@link RankedProvider providers} (custom and default) registered for the given service provider
     * contract in the underlying {@link ServiceLocator HK2 service locator} container.
     *
     * @param       service provider contract Java type.
     * @param locator  underlying HK2 service locator.
     * @param contract service provider contract.
     * @return iterable of all available ranked service providers for the contract. Return value is never null.
     */
    public static  Iterable> getAllRankedProviders(ServiceLocator locator, Class contract) {
        List> providers = getAllServiceHandles(locator, contract, new CustomAnnotationImpl());
        providers.addAll(getAllServiceHandles(locator, contract));

        LinkedHashMap, RankedProvider> providerMap = Maps.newLinkedHashMap();

        for (ServiceHandle provider : providers) {
            ActiveDescriptor key = provider.getActiveDescriptor();
            if (!providerMap.containsKey(key)) {
                providerMap.put(key, new RankedProvider(provider.getService(), key.getRanking()));
            }
        }

        return providerMap.values();
    }

    /**
     * Sorts given providers with {@link RankedComparator ranked comparator}.
     *
     * @param comparator        comparator to sort the providers with.
     * @param providerIterables providers to be sorted.
     * @param                service provider contract Java type.
     * @return sorted {@link Iterable iterable} instance containing given providers. Return value is never null.
     */
    public static  Iterable sortRankedProviders(final RankedComparator comparator,
                                                      final Iterable>... providerIterables) {
        final List> rankedProviders = Lists.newArrayList();

        for (final Iterable> providers : providerIterables) {
            rankedProviders.addAll(Lists.>newLinkedList(providers));
        }

        Collections.sort(rankedProviders, comparator);

        return Collections2.transform(rankedProviders, new Function, T>() {
            @Override
            public T apply(final RankedProvider input) {
                return input.getProvider();
            }
        });
    }

    /**
     * Get the sorted iterable of all {@link RankedProvider providers} (custom and default) registered for the given service
     * provider contract in the underlying {@link ServiceLocator HK2 service locator} container.
     *
     * @param         service provider contract Java type.
     * @param locator    underlying HK2 service locator.
     * @param contract   service provider contract.
     * @param comparator comparator to sort the providers with.
     * @return set of all available ranked service providers for the contract. Return value is never null.
     */
    public static  Iterable getAllProviders(final ServiceLocator locator,
                                                  final Class contract,
                                                  final RankedComparator comparator) {
        //noinspection unchecked
        return sortRankedProviders(comparator, getAllRankedProviders(locator, contract));
    }

    private static  List> getAllServiceHandles(ServiceLocator locator, Class contract,
                                                                   Annotation... qualifiers) {

        List> allServiceHandles = qualifiers == null ?
                locator.getAllServiceHandles(contract) :
                locator.getAllServiceHandles(contract, qualifiers);

        ArrayList> serviceHandles = new ArrayList>();
        for (ServiceHandle handle : allServiceHandles) {
            //noinspection unchecked
            serviceHandles.add((ServiceHandle) handle);
        }
        return serviceHandles;
    }

    /**
     * Get the iterable of all providers (custom and default) registered for the given service provider contract
     * in the underlying {@link ServiceLocator HK2 service locator} container ordered based on the given {@code comparator}.
     *
     * @param         service provider contract Java type.
     * @param locator    underlying HK2 service locator.
     * @param contract   service provider contract.
     * @param comparator comparator to be used for sorting the returned providers.
     * @return set of all available service provider instances for the contract ordered using the given
     *         {@link Comparator comparator}.
     */
    public static  Iterable getAllProviders(ServiceLocator locator, Class contract, Comparator comparator) {
        List> providers = getAllServiceHandles(locator, contract, new CustomAnnotationImpl());
        providers.addAll(getAllServiceHandles(locator, contract));

        LinkedHashMap> providerMap = Maps.newLinkedHashMap();

        for (ServiceHandle provider : providers) {
            ActiveDescriptor key = provider.getActiveDescriptor();
            if (!providerMap.containsKey(key)) {
                providerMap.put(key, provider);
            }
        }

        final ArrayList providerList = new ArrayList(getClasses(providerMap.values()));

        if (comparator != null) {
            Collections.sort(providerList, comparator);
        }

        return providerList;
    }

    private static  Set getClasses(Collection> hk2Providers) {
        if (hk2Providers.isEmpty()) {
            return Sets.newLinkedHashSet();
        } else {
            return Sets.newLinkedHashSet(Collections2.transform(hk2Providers, new ProviderToService()));
        }
    }

    /**
     * Get the set of all providers registered for the given service provider contract
     * in the underlying {@link ServiceLocator HK2 locator} container.
     *
     * @param         service provider contract Java type.
     * @param locator    underlying HK2 service locator.
     * @param contract   service provider contract.
     * @param comparator contract comparator used for ordering contracts in the
     *                   set.
     * @return set of all available service provider instances for the contract.
     */
    public static  SortedSet getProviders(ServiceLocator locator, Class contract, final Comparator comparator) {
        final Collection> hk2Providers = getAllServiceHandles(locator, contract);
        if (hk2Providers.isEmpty()) {
            return Sets.newTreeSet(comparator);
        } else {
            final TreeSet set = Sets.newTreeSet(comparator);
            set.addAll(Collections2.transform(hk2Providers, new ProviderToService()));
            return set;
        }
    }

    /**
     * Returns provider contracts recognized by Jersey that are implemented by the {@code clazz}.
     * Recognized provider contracts include all JAX-RS providers as well as all Jersey SPI
     * components annotated with {@link Contract @Contract} annotation.
     *
     * @param clazz class to extract the provider interfaces from.
     * @return set of provider contracts implemented by the given class.
     */
    public static Set> getProviderContracts(Class clazz) {
        Set> contracts = Sets.newIdentityHashSet();
        computeProviderContracts(clazz, contracts);
        return contracts;
    }

    private static void computeProviderContracts(Class clazz, Set> contracts) {
        for (Class contract : getImplementedContracts(clazz)) {
            if (isSupportedContract(contract)) {
                contracts.add(contract);
            }
            computeProviderContracts(contract, contracts);
        }
    }

    /**
     * Check the {@code component} whether it is appropriate correctly configured for client or server
     * {@link RuntimeType runtime}.
     *
     * If a problem occurs a warning is logged and if the component is not usable at all in the current runtime
     * {@code false} is returned. For classes found during component scanning (scanned=true) certain warnings are
     * completely ignored (e.g. components {@link ConstrainedTo constrained to} the client runtime and found by
     * server-side class path scanning will be silently ignored and no warning will be logged).
     *
     * @param component         the class of the component being checked.
     * @param model             model of the component.
     * @param runtimeConstraint current runtime (client or server).
     * @param scanned           {@code false} if the component type has been registered explicitly;
     *                          {@code true} if the class has been discovered during any form of component scanning.
     * @param isResource        {@code true} if the component is also a resource class.
     * @return {@code true} if component is acceptable for use in the given runtime type, {@code false} otherwise.
     */
    public static boolean checkProviderRuntime(Class component,
                                               ContractProvider model,
                                               RuntimeType runtimeConstraint,
                                               boolean scanned,
                                               boolean isResource) {
        final Set> contracts = model.getContracts();
        final ConstrainedTo constrainedTo = component.getAnnotation(ConstrainedTo.class);
        final RuntimeType componentConstraint = constrainedTo == null ? null : constrainedTo.value();
        if (Feature.class.isAssignableFrom(component)) {
            // TODO: solve after implementation
            return true;
        }

        final StringBuilder warnings = new StringBuilder();
        try {
            /**
             * Indicates that the provider implements at least one contract compatible
             * with it's implementation class constraint.
             */
            boolean foundComponentCompatible = componentConstraint == null;
            boolean foundRuntimeCompatibleContract = isResource && runtimeConstraint == RuntimeType.SERVER;
            for (Class contract : contracts) {
                // if the contract is common/not constrained, default to provider constraint
                final RuntimeType contractConstraint = getContractConstraint(contract, componentConstraint);
                foundRuntimeCompatibleContract |= contractConstraint == null || contractConstraint == runtimeConstraint;

                if (componentConstraint != null) {
                    if (contractConstraint != componentConstraint) {
                        warnings.append(LocalizationMessages.WARNING_PROVIDER_CONSTRAINED_TO_WRONG_PACKAGE(
                                component.getName(),
                                componentConstraint.name(),
                                contract.getName(),
                                contractConstraint.name()))
                                .append(" ");
                    } else {
                        foundComponentCompatible = true;
                    }
                }
            }

            if (!foundComponentCompatible) {
                warnings.append(LocalizationMessages.ERROR_PROVIDER_CONSTRAINED_TO_WRONG_PACKAGE(
                        component.getName(),
                        componentConstraint.name()))
                        .append(" ");
                logProviderSkipped(warnings, component, isResource);
                return false;
            }

            boolean isProviderRuntimeCompatible;
            // runtimeConstraint vs. providerConstraint
            isProviderRuntimeCompatible = componentConstraint == null || componentConstraint == runtimeConstraint;
            if (!isProviderRuntimeCompatible && !scanned) {
                // log failure for manually registered providers
                warnings.append(LocalizationMessages.ERROR_PROVIDER_CONSTRAINED_TO_WRONG_RUNTIME(
                        component.getName(),
                        componentConstraint.name(),
                        runtimeConstraint.name()))
                        .append(" ");

                logProviderSkipped(warnings, component, isResource);
            }

            // runtimeConstraint vs contractConstraint
            if (!foundRuntimeCompatibleContract && !scanned) {
                warnings.append(LocalizationMessages.ERROR_PROVIDER_REGISTERED_WRONG_RUNTIME(
                        component.getName(),
                        runtimeConstraint.name()))
                        .append(" ");
                logProviderSkipped(warnings, component, isResource);
                return false;
            }

            return isProviderRuntimeCompatible && foundRuntimeCompatibleContract;
        } finally {
            if (warnings.length() > 0) {
                LOGGER.log(Level.WARNING, warnings.toString());
            }
        }
    }

    private static void logProviderSkipped(StringBuilder sb, Class provider, boolean alsoResourceClass) {
        sb.append(alsoResourceClass ?
                LocalizationMessages.ERROR_PROVIDER_AND_RESOURCE_CONSTRAINED_TO_IGNORED(provider.getName()) :
                LocalizationMessages.ERROR_PROVIDER_CONSTRAINED_TO_IGNORED(provider.getName()))
                .append(" ");
    }

    /**
     * Check if the given Java type is a Jersey-supported contract.
     *
     * @param type contract type.
     * @return {@code true} if given type is a Jersey-supported contract, {@code false} otherwise.
     */
    public static boolean isSupportedContract(Class type) {
        return (EXTERNAL_PROVIDER_INTERFACE_WHITELIST.get(type) != null || type.isAnnotationPresent(Contract.class));
    }

    private static RuntimeType getContractConstraint(Class clazz, RuntimeType defaultConstraint) {
        final ProviderRuntime jaxRsProvider = EXTERNAL_PROVIDER_INTERFACE_WHITELIST.get(clazz);

        RuntimeType result = null;
        if (jaxRsProvider != null) {
            result = jaxRsProvider.getRuntime();
        } else if (clazz.getAnnotation(Contract.class) != null) {
            final ConstrainedTo constrainedToAnnotation = clazz.getAnnotation(ConstrainedTo.class);
            if (constrainedToAnnotation != null) {
                result = constrainedToAnnotation.value();
            }
        }

        return (result == null) ? defaultConstraint : result;
    }

    private static List> getImplementedContracts(Class clazz) {
        List> list = new LinkedList>();

        Collections.addAll(list, clazz.getInterfaces());

        final Class superclass = clazz.getSuperclass();
        if (superclass != null) {
            list.add(superclass);
        }

        return list;
    }

    /**
     * Returns {@code true} if the given component class is a provider (implements specific interfaces).
     * See {@link #getProviderContracts}.
     *
     * @param clazz class to test.
     * @return {@code true} if the class is provider, {@code false} otherwise.
     */
    public static boolean isProvider(Class clazz) {
        return findFirstProviderContract(clazz);
    }

    /**
     * Returns {@code true} if given component class is a JAX-RS provider.
     *
     * @param clazz class to check.
     * @return {@code true} if the class is a JAX-RS provider, {@code false} otherwise.
     */
    public static boolean isJaxRsProvider(Class clazz) {
        for (Class providerType : JAX_RS_PROVIDER_INTERFACE_WHITELIST.keySet()) {
            if (providerType.isAssignableFrom(clazz)) {
                return true;
            }
        }
        return false;
    }

    private static boolean findFirstProviderContract(Class clazz) {
        for (Class contract : getImplementedContracts(clazz)) {
            if (isSupportedContract(contract)) {
                return true;
            }
            if (findFirstProviderContract(contract)) {
                return true;
            }
        }
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy