Skip to content

Commit

Permalink
immutable, serializable, refactored JavaEEContextUtil and added Payar…
Browse files Browse the repository at this point in the history
…a API for EE context
  • Loading branch information
lprimak committed Nov 28, 2020
1 parent 9758ea0 commit 460aa7c
Show file tree
Hide file tree
Showing 25 changed files with 1,032 additions and 342 deletions.
1 change: 1 addition & 0 deletions api/payara-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
fish.payara.cdi.auth.roles,
fish.payara.cdi.jsr107.impl,
fish.payara.cluster,
fish.payara.context,
fish.payara.jacc,
fish.payara.micro.cdi,
fish.payara.nucleus.requesttracing.api,
Expand Down
171 changes: 171 additions & 0 deletions api/payara-api/src/main/java/fish/payara/context/ContextProducer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) [2020] Payara Foundation 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
* https://github.com/payara/Payara/blob/master/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 glassfish/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* The Payara Foundation designates this particular file as subject to the "Classpath"
* exception as provided by the Payara Foundation 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 fish.payara.context;

import java.io.Serializable;

/**
* Utility to create / push Jakarta EE and CDI thread contexts
*
* Example:
* @Inject ContextProducer producer;
* // EJB/CDI thread:
* Instance saved = producer.currentInvocation*();
* // insure 'saved' is not leaked when application undeployed,
* // otherwise use producer.fromComponentId(producer.getInvocationComponentId())
* // and in another, non EJB/CDI thread:
* try (Context ctx = saved.pushRequestContext()) {
* // runs with EJB / CDI context
* }
*
* @author lprimak
*/
public interface ContextProducer {
/**
* Creates an empty instance, i.e. if the empty context is pushed
* on top of another context, the other context will be 'suppressed'
* for the duration of this context
*
* @return new empty instance
*/
Instance empty();

/**
* captures current invocation and returns it as an instance
*
* @return new captured instance
*/
Instance currentInvocation() throws IllegalStateException;

/**
*
* @param componentId component id for this instance, non-null
*
* @return new instance based on componentId
*/
Instance fromComponentId(String componentId) throws IllegalArgumentException;

/**
* @return Class Loader that's associated with current invocation or null if
* there is no current invocation
*/
ClassLoader getInvocationClassLoader();

/**
* @return component ID for the current invocation or null
*/
String getInvocationComponentId();

/**
* This is different from class loaded, as there are some situations
* where class is loaded but initialization is not complete,
* such as CDI initializations, extensions start, etc.
*
* @return true if current invocation exists and is loaded / ready
*/
boolean isInvocationLoaded();

/**
* specific, immutable, thread-safe instance of the context
*/
interface Instance extends Serializable {
/**
* pushes Java EE invocation context onto the invocation stack use
* try-with-resources to pop the context
* no-op if non-running context
*
* @return the new context that was created
*/
Context pushContext();

/**
* pushes invocation context onto the stack Also creates Request scope
* use try-with-resources to pop the context
* no-op if non-running context
*
* @return new context that was created
*/
Context pushRequestContext();

/**
* set context class loader by component id of this instance
* for empty or unloaded component, class loader remains unset and the
* context is a no-op (no re-set gets done) so it's a no-op
*
* @return context so class loader can be reset
*/
Context setApplicationClassLoader();

/**
* @return component ID for the current instance, or null if empty instance
*/
String getInstanceComponentId();

/**
* This is different from class loaded, as there are some situations
* where class is loaded but initialization is not complete, such as CDI
* initializations, extensions start, etc.
*
* @return true if component is loaded and starting
*/
boolean isLoaded();

/**
* @return true if this is an empty context
*/
boolean isEmpty();

/**
* remove cached invocation from this instance, in case the
* underlying app unloaded but component ID remains, just in case the
* app is reloaded
*/
void clearInstanceInvocation();
}

interface Context extends Closeable {
boolean isValid();
};

interface Closeable extends AutoCloseable {
@Override
public void close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) [2020] Payara Foundation 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
* https://github.com/payara/Payara/blob/master/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 glassfish/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* The Payara Foundation designates this particular file as subject to the "Classpath"
* exception as provided by the Payara Foundation 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 fish.payara.context;

import java.util.stream.Stream;

/**
* Wraps an object with a proxy that sets Payara context around
* every method call. This is useful for listeners, callbacks,
* and other objects that run from another thread that may not have
* Payara context initialized.
*
* This was create primarily to facilitate integration with Hazelcast,
* which has listeners that do not capture Payara context.
*
* Example:
* @Inject Contextualizer contextualizer;
* @Inject MyListener myListener;
*
* // runs myListener.listen() method in a separate thread,
* // with context captured from current thread
* new Thread(contextualizer.contextualize(() -> myListener.listen(), Runnable.class));
*
* @author lprimak
*/
public interface Contextualizer {
/**
* Wraps an object in a proxy that preserves Payara context
*
* @param <T> object type to proxy and contextualize
* @param object object instance to proxy and contextualize
* @param intf Interface that is used to proxy the object
* @return proxied object
*/
<T> T contextualize(T object, Class<T> intf);

/**
* Wraps an object in a proxy that preserves Payara context
*
* @param <T>
* @param object
* @param context
* @param intf
* @return proxied object
*/
<T> T contextualize(T object, ContextProducer.Instance context, Class<T> intf);

/**
* Wraps an object in a proxy that preserves Payara context
*
* @param <T>
* @param object
* @param context
* @param interfaces
* @return proxied object
*/
<T> T contextualize(T object, ContextProducer.Instance context, Stream<Class<?>> interfaces);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016-2019] [Payara Foundation and/or its affiliates]
// Portions Copyright [2016-2020] [Payara Foundation and/or its affiliates]

package com.sun.enterprise.deployment.util;

Expand Down Expand Up @@ -142,8 +142,7 @@ public void event(Event<?> event) {
parseResources(deploymentContext, application, appResources);

// Ensure we have a valid component invocation before triggering lookups
contextUtil.setEmptyInvocation();
try (Context ctx = contextUtil.pushContext()) {
try (Context ctx = contextUtil.empty().pushContext()) {
validateResources(deploymentContext, application, appResources);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2018-2020] [Payara Foundation and/or its affiliates]

package com.sun.ejb;

Expand All @@ -56,14 +57,17 @@ public EjbInvocationFactory(String compEnvId, Container container) {
}

public EjbInvocation create() {
return new EjbInvocation(compEnvId, container);
EjbInvocation ejbInv = new EjbInvocation(compEnvId, container);
ejbInv.jndiEnvironment = container.getEjbDescriptor();
return ejbInv;
}

public <C extends ComponentContext> EjbInvocation create(Object ejb, C ctx) {
EjbInvocation ejbInv = new EjbInvocation(compEnvId, container);
ejbInv.ejb = ejb;
ejbInv.instance = ejb;
ejbInv.context = ctx;
ejbInv.jndiEnvironment = container.getEjbDescriptor();

return ejbInv;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2016-2019 Payara Foundation and/or its affiliates. All rights reserved.
* Copyright (c) 2016-2020 Payara Foundation 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
Expand Down Expand Up @@ -46,8 +46,10 @@
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.Optional;
import org.glassfish.internal.api.Globals;
import org.glassfish.internal.api.JavaEEContextUtil;
import org.glassfish.internal.api.JavaEEContextUtil.Context;

/**
* Packages up an object into a Serializable value
Expand All @@ -66,7 +68,7 @@ public PayaraValueHolder() {

public PayaraValueHolder(T value) throws IOException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(Globals.getDefaultHabitat().getService(JavaEEContextUtil.class).getInstanceComponentId());
oos.writeObject(Globals.getDefaultHabitat().getService(JavaEEContextUtil.class).getInvocationComponentId());
oos.writeObject(value);
data = baos.toByteArray();
}
Expand All @@ -77,17 +79,21 @@ public T getValue() throws IOException, ClassNotFoundException {
String componentId = null;
try (ByteArrayInputStream bais = new ByteArrayInputStream(data); PayaraTCCLObjectInputStream ois = new PayaraTCCLObjectInputStream(bais)) {
componentId = (String)ois.readObject();
Object result = ois.readObject();
return (T)result;
JavaEEContextUtil ctxUtil = Globals.getDefaultHabitat().getService(JavaEEContextUtil.class);
JavaEEContextUtil.Instance inst = Optional.ofNullable(componentId)
.map(ctxUtil::fromComponentId).orElse(ctxUtil.empty());
try (Context ctx = inst.setApplicationClassLoader()) {
return (T) ois.readObject();
}
}
catch (ClassNotFoundException ex) {
String invocationComponentId = Globals.getDefaultHabitat().getService(JavaEEContextUtil.class).getInstanceComponentId();
String invocationComponentId = Globals.getDefaultHabitat().getService(JavaEEContextUtil.class).getInvocationComponentId();
if (componentId == null){
componentId = "";
}
if (componentId.equals(invocationComponentId)) {
throw new ClassNotFoundException(String.format("Wrong application: expected %s bug got %s", componentId, invocationComponentId),
new IllegalStateException("Wrong Application"));
if (!componentId.equals(invocationComponentId)) {
throw new ClassNotFoundException(String.format("Wrong application: expected %s but got %s", componentId, invocationComponentId),
new IllegalStateException("Wrong Application", ex));
} else {
throw ex;
}
Expand Down
Loading

0 comments on commit 460aa7c

Please sign in to comment.