
org.glassfish.faces.integration.GlassFishInjectionProvider Maven / Gradle / Ivy
/*
* Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation.
* Copyright (c) 2009, 2020 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.faces.integration;
import com.sun.enterprise.container.common.spi.CDIService;
import com.sun.enterprise.container.common.spi.util.ComponentEnvManager;
import com.sun.enterprise.container.common.spi.util.InjectionException;
import com.sun.enterprise.container.common.spi.util.InjectionManager;
import com.sun.enterprise.deployment.BundleDescriptor;
import com.sun.enterprise.deployment.InjectionInfo;
import com.sun.enterprise.deployment.JndiNameEnvironment;
import com.sun.enterprise.web.Constants;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.spi.AnnotationScanner;
import com.sun.faces.spi.DiscoverableInjectionProvider;
import com.sun.faces.spi.HighAvailabilityEnabler;
import com.sun.faces.spi.InjectionProviderException;
import com.sun.faces.util.FacesLogger;
import jakarta.servlet.ServletContext;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.api.invocation.InvocationManager;
import org.glassfish.hk2.api.ServiceLocator;
import static java.util.logging.Level.FINE;
import static org.glassfish.api.invocation.ComponentInvocation.ComponentInvocationType.SERVLET_INVOCATION;
/**
*
* This InjectionProvider
is specific to the GlassFish/SJSAS 9.x PE/EE application servers.
*
*/
public class GlassFishInjectionProvider extends DiscoverableInjectionProvider implements AnnotationScanner, HighAvailabilityEnabler {
private static final Logger LOGGER = FacesLogger.APPLICATION.getLogger();
private static final String SERVICELOCATOR_ATTRIBUTE = "org.glassfish.servlet.habitat";
private ComponentEnvManager componentEnvManager;
private InjectionManager injectionManager;
private InvocationManager invocationManager;
private CDIService cdiService;
/**
* Constructs a new GlassFishInjectionProvider
instance.
*
* @param servletContext
*/
public GlassFishInjectionProvider(ServletContext servletContext) {
ServiceLocator defaultServices = (ServiceLocator) servletContext.getAttribute(SERVICELOCATOR_ATTRIBUTE);
componentEnvManager = defaultServices.getService(ComponentEnvManager.class);
invocationManager = defaultServices.getService(InvocationManager.class);
injectionManager = defaultServices.getService(InjectionManager.class);
cdiService = defaultServices.getService(CDIService.class);
}
@Override
public Map> getAnnotatedClassesInCurrentModule(ServletContext servletContext) throws InjectionProviderException {
return null;
}
/**
*
* The implementation of this method must perform the following steps:
*
* - Inject the supported resources per the Servlet 2.5 specification into the provided object
*
*
*
* @param managedBean the target managed bean
*/
@Override
public void inject(Object managedBean) throws InjectionProviderException {
try {
injectionManager.injectInstance(managedBean, getNamingEnvironment(), false);
if (cdiService.isCurrentModuleCDIEnabled()) {
cdiService.injectManagedObject(managedBean, getBundle());
}
} catch (InjectionException ie) {
throw new InjectionProviderException(ie);
}
}
/**
*
* The implemenation of this method must invoke any method marked with the @PreDestroy
annotation (per the
* Common Annotations Specification).
*
* @param managedBean the target managed bean
*/
@Override
public void invokePreDestroy(Object managedBean) throws InjectionProviderException {
try {
injectionManager.invokeInstancePreDestroy(managedBean);
} catch (InjectionException ie) {
throw new InjectionProviderException(ie);
}
}
/**
*
* The implemenation of this method must invoke any method marked with the @PostConstruct
annotation (per
* the Common Annotations Specification).
*
* @param managedBean the target managed bean
*
* @throws com.sun.faces.spi.InjectionProviderException if an error occurs when invoking the method annotated by the
* @PostConstruct
annotation
*/
@Override
public void invokePostConstruct(Object managedBean) throws InjectionProviderException {
try {
this.invokePostConstruct(managedBean, getNamingEnvironment());
} catch (InjectionException ie) {
throw new InjectionProviderException(ie);
}
}
// --------------------------------------------------------- Private Methods
/**
*
* This is based off of code in InjectionManagerImpl
.
*
*
* @return JndiNameEnvironment
* @throws InjectionException if we're unable to obtain the JndiNameEnvironment
*/
private JndiNameEnvironment getNamingEnvironment() throws InjectionException {
ComponentInvocation currentInvocation = invocationManager.getCurrentInvocation();
if (currentInvocation == null) {
throw new InjectionException("null invocation context");
}
if (currentInvocation.getInvocationType() != SERVLET_INVOCATION) {
throw new InjectionException("Wrong invocation type");
}
JndiNameEnvironment componentEnv = (JndiNameEnvironment) currentInvocation.jndiEnvironment;
if (componentEnv != null) {
return componentEnv;
}
throw new InjectionException("No descriptor registered for " + " current invocation : " + currentInvocation);
}
/**
*
* This is based off of code in InjectionManagerImpl
.
*
*
* @param instance managed bean instance
* @param envDescriptor JNDI environment
* @throws InjectionException if an error occurs
*/
private void invokePostConstruct(Object instance, JndiNameEnvironment envDescriptor) throws InjectionException {
LinkedList postConstructMethods = new LinkedList();
Class extends Object> nextClass = instance.getClass();
// Process each class in the inheritance hierarchy, starting with
// the most derived class and ignoring java.lang.Object.
while ((!Object.class.equals(nextClass)) && (nextClass != null)) {
InjectionInfo injInfo = envDescriptor.getInjectionInfoByClass(nextClass);
if (injInfo.getPostConstructMethodName() != null) {
Method postConstructMethod = getPostConstructMethod(injInfo, nextClass);
// Invoke the preDestroy methods starting from
// the least-derived class downward.
postConstructMethods.addFirst(postConstructMethod);
}
nextClass = nextClass.getSuperclass();
}
for (Method postConstructMethod : postConstructMethods) {
invokeLifecycleMethod(postConstructMethod, instance);
}
}
/**
*
* This is based off of code in InjectionManagerImpl
.
*
*
* @param injectionInfo InjectionInfo
* @param resourceClass target class
* @return a Method marked with the @PostConstruct annotation
* @throws InjectionException if an error occurs
*/
private Method getPostConstructMethod(InjectionInfo injectionInfo, Class extends Object> resourceClass) throws InjectionException {
Method postConstructMethod = injectionInfo.getPostConstructMethod();
if (postConstructMethod == null) {
String postConstructMethodName = injectionInfo.getPostConstructMethodName();
// Check for the method within the resourceClass only.
// This does not include super-classses.
for (Method next : resourceClass.getDeclaredMethods()) {
// InjectionManager only handles injection into PostConstruct
// methods with no arguments.
if (next.getName().equals(postConstructMethodName) && (next.getParameterTypes().length == 0)) {
postConstructMethod = next;
injectionInfo.setPostConstructMethod(postConstructMethod);
break;
}
}
}
if (postConstructMethod == null) {
throw new InjectionException("InjectionManager exception. PostConstruct method " + injectionInfo.getPostConstructMethodName()
+ " could not be found in class " + injectionInfo.getClassName());
}
return postConstructMethod;
}
/**
*
* This is based off of code in InjectionManagerImpl
.
*
*
* @param lifecycleMethod the method to invoke
* @param instance the instanced to invoke the method against
* @throws InjectionException if an error occurs
*/
private void invokeLifecycleMethod(final Method lifecycleMethod, final Object instance) throws InjectionException {
try {
if (LOGGER.isLoggable(FINE)) {
LOGGER.fine("Calling lifecycle method " + lifecycleMethod + " on class " + lifecycleMethod.getDeclaringClass());
}
// Wrap actual value insertion in doPrivileged to
// allow for private/protected field access.
if (!lifecycleMethod.trySetAccessible()) {
throw new InaccessibleObjectException("Unable to make accessible: " + lifecycleMethod);
}
lifecycleMethod.invoke(instance);
} catch (Exception t) {
String msg = "Exception attempting invoke lifecycle " + " method " + lifecycleMethod;
LOGGER.log(FINE, msg, t);
InjectionException ie = new InjectionException(msg);
Throwable cause = (t instanceof InvocationTargetException) ? t.getCause() : t;
ie.initCause(cause);
throw ie;
}
return;
}
private BundleDescriptor getBundle() {
JndiNameEnvironment env = componentEnvManager.getCurrentJndiNameEnvironment();
BundleDescriptor bundle = null;
if (env instanceof BundleDescriptor) {
bundle = (BundleDescriptor) env;
}
if (bundle == null) {
throw new IllegalStateException("Invalid context for managed bean creation");
}
return bundle;
}
/**
* Method to test with HA has been enabled. If so, then set the JSF context param
* com.sun.faces.enableAgressiveSessionDirtying to true
*
* @param ctx
*/
@Override
public void enableHighAvailability(ServletContext ctx) {
// look at the following values for the web app
// 1> has in the web.xml
// 2> Was deployed with --availabilityenabled --target
WebConfiguration config = WebConfiguration.getInstance(ctx);
if (!config.isSet(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable )) {
Object isDistributableObj = ctx.getAttribute(Constants.IS_DISTRIBUTABLE_ATTRIBUTE);
Object enableHAObj = ctx.getAttribute(Constants.ENABLE_HA_ATTRIBUTE);
if (isDistributableObj instanceof Boolean && enableHAObj instanceof Boolean) {
boolean isDistributable = ((Boolean) isDistributableObj).booleanValue();
boolean enableHA = ((Boolean) enableHAObj).booleanValue();
if (LOGGER.isLoggable(FINE)) {
LOGGER.log(FINE, "isDistributable = {0} enableHA = {1}", new Object[] { isDistributable, enableHA });
}
if (isDistributable && enableHA) {
LOGGER.fine("setting the EnableAgressiveSessionDirtying to true");
config.overrideContextInitParameter(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable,
Boolean.TRUE);
}
}
}
}
}