-
Notifications
You must be signed in to change notification settings - Fork 566
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New RequestScopeHelper class to track request scopes across threads (#…
…2856) * Run concurrent requests for each test. Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com> * New test. Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com> * New helper class to manage request scope logic across threads. Update of thread local variable in Jersey to make sure correct InjectionManager is used in non-request threads. Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com> * Updated test to show the problem described in #2632 using multiple applications. Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com> * Fixed copyright. Signed-off-by: Santiago Pericasgeertsen <santiago.pericasgeertsen@oracle.com>
- Loading branch information
1 parent
9ca87ed
commit bf9f256
Showing
13 changed files
with
421 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
137 changes: 137 additions & 0 deletions
137
...lt-tolerance/src/main/java/io/helidon/microprofile/faulttolerance/RequestScopeHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
* Copyright (c) 2021 Oracle and/or its affiliates. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.helidon.microprofile.faulttolerance; | ||
|
||
import java.util.concurrent.Callable; | ||
|
||
import javax.enterprise.context.control.RequestContextController; | ||
import javax.enterprise.inject.Instance; | ||
import javax.enterprise.inject.spi.CDI; | ||
|
||
import org.glassfish.jersey.internal.inject.InjectionManager; | ||
import org.glassfish.jersey.process.internal.RequestContext; | ||
import org.glassfish.jersey.process.internal.RequestScope; | ||
import org.glassfish.jersey.weld.se.WeldRequestScope; | ||
|
||
class RequestScopeHelper { | ||
|
||
enum State { | ||
CLEARED, | ||
STORED | ||
} | ||
|
||
private State state = State.CLEARED; | ||
|
||
/** | ||
* CDI's request scope controller used for activation/deactivation. | ||
*/ | ||
private RequestContextController requestController; | ||
|
||
/** | ||
* Jersey's request scope object. Will be non-null if request scope is active. | ||
*/ | ||
private RequestScope requestScope; | ||
|
||
/** | ||
* Jersey's request scope object. | ||
*/ | ||
private RequestContext requestContext; | ||
|
||
/** | ||
* Jersey's injection manager. | ||
*/ | ||
private InjectionManager injectionManager; | ||
|
||
/** | ||
* Store request context information from the current thread. State | ||
* related to Jersey and CDI to handle {@code @Context} and {@code @Inject} | ||
* injections. | ||
*/ | ||
void saveScope() { | ||
if (state == State.STORED) { | ||
throw new IllegalStateException("Request scope state already stored"); | ||
} | ||
// CDI scope | ||
Instance<RequestContextController> rcc = CDI.current().select(RequestContextController.class); | ||
if (rcc.isResolvable()) { | ||
requestController = rcc.get(); | ||
} | ||
// Jersey scope | ||
injectionManager = WeldRequestScope.actualInjectorManager.get(); // thread local | ||
try { | ||
requestScope = CDI.current().select(RequestScope.class).get(); | ||
requestContext = requestScope.referenceCurrent(); | ||
} catch (Exception e) { | ||
// Ignored, Jersey request scope not active | ||
} finally { | ||
state = State.STORED; | ||
} | ||
} | ||
|
||
/** | ||
* Wraps a supplier into another supplier that actives the request scope | ||
* before calling it. | ||
* | ||
* @param supplier supplier to wrap | ||
* @return wrapped supplier | ||
*/ | ||
FtSupplier<Object> wrapInScope(FtSupplier<Object> supplier) { | ||
if (state != State.STORED) { | ||
throw new IllegalStateException("Request scope state never stored"); | ||
} | ||
if (requestScope != null) { // Jersey and CDI | ||
return () -> requestScope.runInScope(requestContext, | ||
(Callable<?>) (() -> { | ||
InjectionManager old = WeldRequestScope.actualInjectorManager.get(); | ||
try { | ||
requestController.activate(); | ||
WeldRequestScope.actualInjectorManager.set(injectionManager); | ||
return supplier.get(); | ||
} catch (Throwable t) { | ||
throw t instanceof Exception ? ((Exception) t) : new RuntimeException(t); | ||
} finally { | ||
requestController.deactivate(); | ||
WeldRequestScope.actualInjectorManager.set(old); | ||
} | ||
})); | ||
} else if (requestController != null) { // CDI only | ||
return () -> { | ||
try { | ||
requestController.activate(); | ||
return supplier.get(); | ||
} finally { | ||
requestController.deactivate(); | ||
} | ||
}; | ||
} else { | ||
return supplier; | ||
} | ||
} | ||
|
||
/** | ||
* Clears internal state saved by calling {@link #saveScope()}. | ||
*/ | ||
void clearScope() { | ||
if (requestContext != null) { | ||
requestContext.release(); | ||
requestContext = null; | ||
} | ||
requestScope = null; | ||
requestController = null; | ||
injectionManager = null; | ||
state = State.CLEARED; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
...al/request-scope/src/main/java/io/helidon/tests/functional/requestscope/Application1.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright (c) 2021 Oracle and/or its affiliates. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package io.helidon.tests.functional.requestscope; | ||
|
||
import java.util.Set; | ||
|
||
import javax.enterprise.context.ApplicationScoped; | ||
import javax.ws.rs.core.Application; | ||
|
||
/** | ||
* This functional test requires having two application subclasses. | ||
* See: https://github.com/oracle/helidon/issues/2632#issuecomment-796831904 | ||
*/ | ||
@ApplicationScoped | ||
class Application1 extends Application { | ||
|
||
@Override | ||
public Set<Class<?>> getClasses() { | ||
return Set.of(Service1.class, Service2.class, Service3.class); | ||
} | ||
} |
Oops, something went wrong.