/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2017 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.inject.hk2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.glassfish.jersey.internal.inject.Binding;
import org.glassfish.jersey.internal.inject.ClassBinding;
import org.glassfish.jersey.internal.inject.ForeignDescriptor;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.InstanceBinding;
import org.glassfish.jersey.internal.inject.LocalizationMessages;
import org.glassfish.jersey.internal.inject.ServiceHolder;
import org.glassfish.jersey.internal.inject.ServiceHolderImpl;
import org.glassfish.hk2.api.ActiveDescriptor;
import org.glassfish.hk2.api.Descriptor;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.ServiceLocatorFactory;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.jvnet.hk2.external.runtime.ServiceLocatorRuntimeBean;
/**
* Abstract class dedicated to implementations of {@link InjectionManager} providing several convenient methods.
*
* @author Petr Bouda (petr.bouda at oracle.com)
*/
abstract class AbstractHk2InjectionManager implements InjectionManager {
private static final Logger LOGGER = Logger.getLogger(AbstractHk2InjectionManager.class.getName());
private static final ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
private ServiceLocator locator;
/**
* Private constructor.
*
* @param parent parent of type {@link org.glassfish.jersey.internal.inject.InjectionManager} or {@link ServiceLocator}.
*/
AbstractHk2InjectionManager(Object parent) {
ServiceLocator parentLocator = resolveServiceLocatorParent(parent);
this.locator = createLocator(parentLocator);
// Register all components needed for proper HK2 locator bootstrap
ServiceLocatorUtilities.bind(locator, new Hk2BootstrapBinder(locator));
this.locator.setDefaultClassAnalyzerName(JerseyClassAnalyzer.NAME);
// clear HK2 caches
ServiceLocatorRuntimeBean serviceLocatorRuntimeBean = locator.getService(ServiceLocatorRuntimeBean.class);
if (serviceLocatorRuntimeBean != null) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine(LocalizationMessages.HK_2_CLEARING_CACHE(serviceLocatorRuntimeBean.getServiceCacheSize(),
serviceLocatorRuntimeBean.getReflectionCacheSize()));
}
serviceLocatorRuntimeBean.clearReflectionCache();
serviceLocatorRuntimeBean.clearServiceCache();
}
}
/**
* Creates a new {@link ServiceLocator} instance from static {@link ServiceLocatorFactory} and adds the provided parent
* locator if the instance is not null.
*
* @param parentLocator parent locator, can be {@code null}.
* @return new instance of injection manager.
*/
private static ServiceLocator createLocator(ServiceLocator parentLocator) {
ServiceLocator result = factory.create(null, parentLocator, null, ServiceLocatorFactory.CreatePolicy.DESTROY);
result.setNeutralContextClassLoader(false);
ServiceLocatorUtilities.enablePerThreadScope(result);
return result;
}
private static ServiceLocator resolveServiceLocatorParent(Object parent) {
assertParentLocatorType(parent);
ServiceLocator parentLocator = null;
if (parent != null) {
if (parent instanceof ServiceLocator) {
parentLocator = (ServiceLocator) parent;
} else if (parent instanceof AbstractHk2InjectionManager) {
parentLocator = ((AbstractHk2InjectionManager) parent).getServiceLocator();
}
}
return parentLocator;
}
/**
* Checks if the parent is null then must be an instance of {@link ServiceLocator} or {@link InjectionManager}.
*
* @param parent object represented by {@code ServiceLocator} or {@code HK2InjectionManager}.
*/
private static void assertParentLocatorType(Object parent) {
if (parent != null && !(parent instanceof ServiceLocator || parent instanceof AbstractHk2InjectionManager)) {
throw new IllegalArgumentException(
LocalizationMessages.HK_2_UNKNOWN_PARENT_INJECTION_MANAGER(parent.getClass().getSimpleName()));
}
}
public ServiceLocator getServiceLocator() {
return locator;
}
@Override
public boolean isRegistrable(Class<?> clazz) {
return org.glassfish.hk2.utilities.Binder.class.isAssignableFrom(clazz);
}
@Override
@SuppressWarnings("unchecked")
public <T> List<ServiceHolder<T>> getAllServiceHolders(Class<T> contract, Annotation... qualifiers) {
return getServiceLocator().getAllServiceHandles(contract, qualifiers).stream()
.map(sh -> new ServiceHolderImpl<>(
sh.getService(),
(Class<T>) sh.getActiveDescriptor().getImplementationClass(),
sh.getActiveDescriptor().getContractTypes(),
sh.getActiveDescriptor().getRanking()))
.collect(Collectors.toList());
}
@Override
public <T> T getInstance(Class<T> clazz, Annotation... annotations) {
return getServiceLocator().getService(clazz, annotations);
}
@Override
public <T> T getInstance(Type clazz) {
return getServiceLocator().getService(clazz);
}
@Override
public Object getInstance(ForeignDescriptor foreignDescriptor) {
return getServiceLocator().getServiceHandle((ActiveDescriptor<?>) foreignDescriptor.get()).getService();
}
@Override
public <T> T getInstance(Class<T> clazz) {
return getServiceLocator().getService(clazz);
}
@Override
public <T> T getInstance(Class<T> clazz, String classAnalyzer) {
return getServiceLocator().getService(clazz, classAnalyzer);
}
@Override
public <T> List<T> getAllInstances(Type clazz) {
return getServiceLocator().getAllServices(clazz);
}
@Override
public void preDestroy(Object preDestroyMe) {
getServiceLocator().preDestroy(preDestroyMe);
}
@Override
public void shutdown() {
if (factory.find(getServiceLocator().getName()) != null) {
factory.destroy(getServiceLocator().getName());
} else {
getServiceLocator().shutdown();
}
}
@Override
public <U> U createAndInitialize(Class<U> clazz) {
return getServiceLocator().createAndInitialize(clazz);
}
@Override
public ForeignDescriptor createForeignDescriptor(Binding binding) {
ForeignDescriptor foreignDescriptor = createAndTranslateForeignDescriptor(binding);
ActiveDescriptor<Object> activeDescriptor = ServiceLocatorUtilities
.addOneDescriptor(getServiceLocator(), (Descriptor) foreignDescriptor.get(), false);
return ForeignDescriptor.wrap(activeDescriptor, activeDescriptor::dispose);
}
@Override
public void inject(Object injectMe) {
getServiceLocator().inject(injectMe);
}
@Override
public void inject(Object injectMe, String classAnalyzer) {
getServiceLocator().inject(injectMe, classAnalyzer);
}
@SuppressWarnings("unchecked")
private ForeignDescriptor createAndTranslateForeignDescriptor(Binding binding) {
ActiveDescriptor activeDescriptor;
if (ClassBinding.class.isAssignableFrom(binding.getClass())) {
activeDescriptor = Hk2Helper.translateToActiveDescriptor((ClassBinding<?>) binding);
} else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) {
activeDescriptor = Hk2Helper.translateToActiveDescriptor((InstanceBinding<?>) binding);
} else {
throw new RuntimeException(org.glassfish.jersey.internal.LocalizationMessages
.UNKNOWN_DESCRIPTOR_TYPE(binding.getClass().getSimpleName()));
}
return ForeignDescriptor.wrap(activeDescriptor, activeDescriptor::dispose);
}
}