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

org.glassfish.jersey.inject.hk2.Hk2Helper 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) 2017, 2024 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.inject.hk2;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import org.glassfish.hk2.api.Factory;
import org.glassfish.jersey.internal.LocalizationMessages;
import org.glassfish.jersey.internal.inject.AliasBinding;
import org.glassfish.jersey.internal.inject.Binding;
import org.glassfish.jersey.internal.inject.Bindings;
import org.glassfish.jersey.internal.inject.ClassBinding;
import org.glassfish.jersey.internal.inject.DisposableSupplier;
import org.glassfish.jersey.internal.inject.InjectionResolverBinding;
import org.glassfish.jersey.internal.inject.InstanceBinding;
import org.glassfish.jersey.internal.inject.PerLookup;
import org.glassfish.jersey.internal.inject.PerThread;
import org.glassfish.jersey.internal.inject.SupplierClassBinding;
import org.glassfish.jersey.internal.inject.SupplierInstanceBinding;

import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.DynamicConfiguration;
import org.glassfish.hk2.api.DynamicConfigurationService;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.AbstractActiveDescriptor;
import org.glassfish.hk2.utilities.ActiveDescriptorBuilder;
import org.glassfish.hk2.utilities.Binder;
import org.glassfish.hk2.utilities.BuilderHelper;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.hk2.utilities.binding.ServiceBindingBuilder;
import org.glassfish.hk2.utilities.reflection.ParameterizedTypeImpl;

/**
 * This class contains the convenient methods for translation from jersey classes to HK2 and visa versa, then contains methods
 * for binding structures such as {@link org.glassfish.jersey.internal.inject.Binder} or {@link Binding} to a provided service
 * locator.
 */
class Hk2Helper {

    /**
     * Bind a translated Jersey-like {@link org.glassfish.jersey.internal.inject.Binder} to HK2-like {@link Binder}.
     *
     * @param injectionManager HK2 injection manager.
     * @param jerseyBinder     Jersey-like binder.
     */
    static void bind(AbstractHk2InjectionManager injectionManager, org.glassfish.jersey.internal.inject.Binder jerseyBinder) {
        bind(injectionManager.getServiceLocator(), Bindings.getBindings(injectionManager, jerseyBinder));
    }

    /**
     * Bind descriptors to Hk2-like {@link Binder}.
     *
     * @param locator HK2 locator.
     * @param binding single descriptor.
     */
    static void bind(ServiceLocator locator, Binding binding) {
        bindBinding(locator, binding);
    }

    /**
     * Bind descriptors to Hk2-like {@link Binder}.
     *
     * @param locator     HK2 locator.
     * @param descriptors collection of descriptors.
     */
    static void bind(ServiceLocator locator, Iterable descriptors) {
        DynamicConfiguration dc = getDynamicConfiguration(locator);
        for (Binding binding : descriptors) {
            bindBinding(locator, dc, binding);
        }
        dc.commit();
    }

    /**
     * Gets {@link DynamicConfigurationService} object from {@link ServiceLocator HK2 Locator} and creates a new object of
     * {@link DynamicConfiguration} to bind new services.
     *
     * @param locator HK2 locator.
     * @return new instance of {@code DynamicConfiguration} to bind new services.
     */
    private static DynamicConfiguration getDynamicConfiguration(ServiceLocator locator) {
        DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
        return dcs.createDynamicConfiguration();
    }

    /**
     * Binds the single descriptor using a single {@link DynamicConfiguration}.
     *
     * @param locator HK2 injection manager.
     * @param binding Jersey descriptor as a holder of information about an injection point.
     */
    private static void bindBinding(ServiceLocator locator, Binding binding) {
        DynamicConfiguration dc = getDynamicConfiguration(locator);
        bindBinding(locator, dc, binding);
        dc.commit();
    }

    /**
     * Binds the single descriptor using an external {@link DynamicConfiguration}.
     *
     * @param locator HK2 injection manager.
     * @param dc      HK2 Dynamic configuration to bind the object.
     * @param binding Jersey descriptor as a holder of information about an injection point.
     */
    private static  void bindBinding(ServiceLocator locator, DynamicConfiguration dc, Binding binding) {
        if (ClassBinding.class.isAssignableFrom(binding.getClass())) {
            final Class implClass = binding.getImplementationType();
            if (Factory.class.isAssignableFrom(implClass)) {
                final Class> factoryClass = (Class>) implClass;
                bindFactory(locator, binding, (binder) -> binder.bindFactory(factoryClass));
            } else {
                ActiveDescriptor activeDescriptor = translateToActiveDescriptor((ClassBinding) binding);
                bindBinding(locator, dc, activeDescriptor, binding.getAliases());
            }

        } else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) {
            if (Factory.class.isAssignableFrom(binding.getImplementationType())) {
                final Factory factory = (Factory) ((InstanceBinding) binding).getService();
                bindFactory(locator, binding, (binder) -> binder.bindFactory(factory));
            } else {
                ActiveDescriptor activeDescriptor = translateToActiveDescriptor((InstanceBinding) binding);
                bindBinding(locator, dc, activeDescriptor, binding.getAliases());
            }

        } else if (InjectionResolverBinding.class.isAssignableFrom(binding.getClass())) {
            InjectionResolverBinding resolverDescriptor = (InjectionResolverBinding) binding;
            bindBinding(locator, dc, wrapInjectionResolver(resolverDescriptor), binding.getAliases());
            bindBinding(locator, dc, translateToActiveDescriptor(resolverDescriptor), binding.getAliases());

        } else if (SupplierClassBinding.class.isAssignableFrom(binding.getClass())) {
            bindSupplierClassBinding(locator, (SupplierClassBinding) binding);

        } else if (SupplierInstanceBinding.class.isAssignableFrom(binding.getClass())) {
            bindSupplierInstanceBinding(locator, (SupplierInstanceBinding) binding);

        } else {
            throw new RuntimeException(LocalizationMessages.UNKNOWN_DESCRIPTOR_TYPE(binding.getClass().getSimpleName()));
        }
    }

    @SuppressWarnings("unchecked")
    private static ActiveDescriptor wrapInjectionResolver(InjectionResolverBinding resolverDescriptor) {
        InjectionResolverWrapper wrappedResolver = new InjectionResolverWrapper<>(resolverDescriptor.getResolver());
        return translateToActiveDescriptor(Bindings.service(wrappedResolver),
                new ParameterizedTypeImpl(InjectionResolver.class, resolverDescriptor.getResolver().getAnnotation()));
    }

    /**
     * Registers a new instance {@link Binder} using the information from the Jersey binding {@link SupplierInstanceBinding}.
     *
     * @param locator HK2 instance manager.
     * @param binding Jersey descriptor as a holder of information about an injection point.
     */
    private static void bindSupplierInstanceBinding(ServiceLocator locator, SupplierInstanceBinding binding) {
        Consumer bindConsumer = binder -> {
            Supplier supplier = binding.getSupplier();
            boolean disposable = DisposableSupplier.class.isAssignableFrom(supplier.getClass());
            // Bind the Supplier itself to be able to inject - Supplier or DisposableSupplier;
            // The contract of the supplier is not registered that means that the instance of the supplier can be retrieved
            // only using Supplier interface and not using implementation class itself. Supplier can be registered only once with
            // all provided contracts.
            AbstractActiveDescriptor> supplierBuilder = BuilderHelper.createConstantDescriptor(supplier);
            binding.getContracts().forEach(contract -> {
                supplierBuilder.addContractType(new ParameterizedTypeImpl(Supplier.class, contract));
                if (disposable) {
                    supplierBuilder.addContractType(new ParameterizedTypeImpl(DisposableSupplier.class, contract));
                }
            });
            // Always call SupplierFactoryBridge.
            supplierBuilder.setName(binding.getName());
            binding.getQualifiers().forEach(supplierBuilder::addQualifierAnnotation);
            binder.bind(supplierBuilder);

            // Register wrapper for factory functionality, wrapper automatically call service locator which is able to retrieve
            // the service in the proper context and scope. Bridge is registered for all contracts but is able to lookup from
            // service locator only using the first contract.
            ServiceBindingBuilder builder = binder.bindFactory(new InstanceSupplierFactoryBridge<>(supplier, disposable));
            setupSupplierFactoryBridge(binding, builder);
        };

        ServiceLocatorUtilities.bind(locator, createBinder(bindConsumer));
    }

    /**
     * Registers a new instance {@link Binder} using the information from the Jersey binding {@link SupplierClassBinding}.
     *
     * @param locator HK2 instance manager.
     * @param binding Jersey descriptor as a holder of information about an injection point.
     */
    private static void bindSupplierClassBinding(ServiceLocator locator, SupplierClassBinding binding) {
        Consumer bindConsumer = binder -> {
            boolean disposable = DisposableSupplier.class.isAssignableFrom(binding.getSupplierClass());

            // Bind the Supplier itself to be able to inject - Supplier supplier;
            // The contract of the supplier is not registered that means that the instance of the supplier can be retrieved
            // only using Supplier interface and not using implementation class itself. Supplier can be registered only once with
            // all provided contracts.
            ServiceBindingBuilder supplierBuilder = binder.bind(binding.getSupplierClass());
            binding.getContracts().forEach(contract -> {
                supplierBuilder.to(new ParameterizedTypeImpl(Supplier.class, contract));
                if (disposable) {
                    supplierBuilder.to(new ParameterizedTypeImpl(DisposableSupplier.class, contract));
                }
            });
            binding.getQualifiers().forEach(supplierBuilder::qualifiedBy);
            supplierBuilder.named(binding.getName());
            supplierBuilder.in(transformScope(binding.getSupplierScope()));
            binder.bind(supplierBuilder);

            // Register wrapper for factory functionality, wrapper automatically call service locator which is able to retrieve
            // the service in the proper context and scope. Bridge is registered for all contracts but is able to lookup from
            // service locator only using the first contract.
            Type contract = null;
            if (binding.getContracts().iterator().hasNext()) {
                contract = binding.getContracts().iterator().next();
            }

            ServiceBindingBuilder builder = binder.bindFactory(
                    new SupplierFactoryBridge<>(locator, contract, binding.getName(), disposable));
            setupSupplierFactoryBridge(binding, builder);
            if (binding.getImplementationType() != null) {
                builder.asType(binding.getImplementationType());
            }
        };

        ServiceLocatorUtilities.bind(locator, createBinder(bindConsumer));
    }

    private static  void bindFactory(ServiceLocator locator,
                                        Binding binding,
                                        Function builder) {
        ServiceLocatorUtilities
                .bind(locator, createBinder((binder) -> setupSupplierFactoryBridge(binding, builder.apply(binder))));
    }

    private static void setupSupplierFactoryBridge(Binding binding, ServiceBindingBuilder builder) {
        builder.named(binding.getName());
        binding.getContracts().forEach(builder::to);
        binding.getQualifiers().forEach(builder::qualifiedBy);
        builder.in(transformScope(binding.getScope()));

        if (binding.getRank() != null) {
            builder.ranked(binding.getRank());
        }

        if (binding.isProxiable() != null) {
            builder.proxy(binding.isProxiable());
        }

        if (binding.isProxiedForSameScope() != null) {
            builder.proxyForSameScope(binding.isProxiedForSameScope());
        }
    }

    static ActiveDescriptor translateToActiveDescriptor(ClassBinding desc) {
        ActiveDescriptorBuilder binding = BuilderHelper.activeLink(desc.getService()).named(desc.getName())
                .analyzeWith(desc.getAnalyzer());

        if (desc.getScope() != null) {
            binding.in(transformScope(desc.getScope()));
        }

        if (desc.getRank() != null) {
            binding.ofRank(desc.getRank());
        }

        for (Annotation annotation : desc.getQualifiers()) {
            binding.qualifiedBy(annotation);
        }

        for (Type contract : desc.getContracts()) {
            binding.to(contract);
        }

        if (desc.isProxiable() != null) {
            binding.proxy(desc.isProxiable());
        }

        if (desc.isProxiedForSameScope() != null) {
            binding.proxyForSameScope(desc.isProxiedForSameScope());
        }

        if (desc.getImplementationType() != null) {
            binding.asType(desc.getImplementationType());
        }

        return binding.build();
    }

    /**
     * Binds a new instance {@link Binding} using the information from the Jersey descriptor {@link InstanceBinding}.
     * 

* Along with a new instance, the method is able to register aliases belonging to the new service. * * @param locator HK2 injection manager. * @param dc HK2 Dynamic configuration to bind the object. * @param activeDescriptor HK2 active descriptor. * @param aliases aliases belonging to the given descriptor. */ private static void bindBinding(ServiceLocator locator, DynamicConfiguration dc, ActiveDescriptor activeDescriptor, Set aliases) { ActiveDescriptor boundDescriptor = dc.bind(activeDescriptor); for (AliasBinding alias : aliases) { dc.bind(createAlias(locator, boundDescriptor, alias)); } } static ActiveDescriptor translateToActiveDescriptor(InstanceBinding desc, Type... contracts) { AbstractActiveDescriptor binding; if (contracts.length == 0) { binding = BuilderHelper.createConstantDescriptor(desc.getService()); } else { binding = BuilderHelper.createConstantDescriptor(desc.getService(), null, contracts); } binding.setName(desc.getName()); binding.setClassAnalysisName(desc.getAnalyzer()); if (desc.getScope() != null) { binding.setScope(desc.getScope().getName()); } if (desc.getRank() != null) { binding.setRanking(desc.getRank()); } for (Annotation annotation : desc.getQualifiers()) { binding.addQualifierAnnotation(annotation); } for (Type contract : desc.getContracts()) { binding.addContractType(contract); } if (desc.isProxiable() != null) { binding.setProxiable(desc.isProxiable()); } if (desc.isProxiedForSameScope() != null) { binding.setProxyForSameScope(desc.isProxiedForSameScope()); } return binding; } private static ActiveDescriptor translateToActiveDescriptor(InjectionResolverBinding desc) { ParameterizedTypeImpl parameterizedType = new ParameterizedTypeImpl( org.glassfish.jersey.internal.inject.InjectionResolver.class, desc.getResolver().getAnnotation()); return BuilderHelper.createConstantDescriptor(desc.getResolver(), null, parameterizedType); } /** * Creates the alias object to a provided descriptor. * * @param locator locator used to create an alias. * @param descriptor descriptor which the alias belongs to. * @param alias source of the alias information. * @return populated alias object, ready to bindBinder using {@link DynamicConfiguration}. */ private static org.glassfish.hk2.utilities.AliasDescriptor createAlias(ServiceLocator locator, ActiveDescriptor descriptor, AliasBinding alias) { org.glassfish.hk2.utilities.AliasDescriptor hk2Alias = new org.glassfish.hk2.utilities.AliasDescriptor<>(locator, descriptor, alias.getContract().getName(), null); alias.getQualifiers().forEach(hk2Alias::addQualifierAnnotation); alias.getScope().ifPresent(hk2Alias::setScope); alias.getRank().ifPresent(hk2Alias::setRanking); return hk2Alias; } /** * Creates a new binder and automatically use it to bind the the descriptors in {@code bindConsumer}. * * @param bindConsumer consumer used to process the defined operation with a binder. * @return populated binder. */ private static Binder createBinder(Consumer bindConsumer) { return new AbstractBinder() { @Override protected void configure() { bindConsumer.accept(this); } }; } /** * Transforms Jersey scopes/annotations to HK2 equivalents. * * @param scope Jersey scope/annotation. * @return HK2 equivalent scope/annotation. */ private static Class transformScope(Class scope) { if (scope == PerLookup.class) { return org.glassfish.hk2.api.PerLookup.class; } else if (scope == PerThread.class) { return org.glassfish.hk2.api.PerThread.class; } return scope; } }