Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 47d1a5e

Browse files
author
Petr Bouda
committed
Replace HK2 Cache implementation by Jersey implementation.
Change-Id: Iab65e056e8f52228f390e27cc4776892e0b2f2f1
1 parent a09ccae commit 47d1a5e

File tree

7 files changed

+285
-42
lines changed

7 files changed

+285
-42
lines changed

core-common/src/main/java/org/glassfish/jersey/hk2/ContextInjectionResolverImpl.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.util.HashSet;
4646
import java.util.List;
4747
import java.util.Set;
48+
import java.util.function.Function;
4849

4950
import javax.ws.rs.core.Context;
5051
import javax.ws.rs.core.GenericType;
@@ -55,6 +56,7 @@
5556
import org.glassfish.jersey.internal.inject.ForeignRequestScopeBridge;
5657
import org.glassfish.jersey.internal.inject.SupplierFactory;
5758
import org.glassfish.jersey.internal.util.ReflectionHelper;
59+
import org.glassfish.jersey.internal.util.collection.Cache;
5860
import org.glassfish.jersey.internal.util.collection.LazyValue;
5961
import org.glassfish.jersey.internal.util.collection.Value;
6062
import org.glassfish.jersey.internal.util.collection.Values;
@@ -71,8 +73,6 @@
7173
import org.glassfish.hk2.utilities.AbstractActiveDescriptor;
7274
import org.glassfish.hk2.utilities.BuilderHelper;
7375
import org.glassfish.hk2.utilities.InjecteeImpl;
74-
import org.glassfish.hk2.utilities.cache.Cache;
75-
import org.glassfish.hk2.utilities.cache.Computable;
7676

7777
/**
7878
* Injection resolver for {@link Context @Context} injection annotation.
@@ -102,10 +102,10 @@ protected void configure() {
102102
private ServiceLocator serviceLocator;
103103

104104
private final Cache<Injectee, ActiveDescriptor<?>> descriptorCache
105-
= new Cache<Injectee, ActiveDescriptor<?>>(new Computable<Injectee, ActiveDescriptor<?>>() {
105+
= new Cache<Injectee, ActiveDescriptor<?>>(new Function<Injectee, ActiveDescriptor<?>>() {
106106

107107
@Override
108-
public ActiveDescriptor<?> compute(Injectee key) {
108+
public ActiveDescriptor<?> apply(Injectee key) {
109109
return serviceLocator.getInjecteeDescriptor(key);
110110
}
111111
});
@@ -119,10 +119,10 @@ public Object resolve(Injectee injectee, ServiceHandle<?> root) {
119119
if (isHk2Factory) {
120120
newInjectee = getFactoryInjectee(injectee, ReflectionHelper.getTypeArgument(requiredType, 0));
121121
} else {
122-
newInjectee = foreignRequestScopedInjecteeCache.compute(injectee);
122+
newInjectee = foreignRequestScopedInjecteeCache.apply(injectee);
123123
}
124124

125-
ActiveDescriptor<?> ad = descriptorCache.compute(newInjectee);
125+
ActiveDescriptor<?> ad = descriptorCache.apply(newInjectee);
126126

127127
if (ad != null) {
128128
final ServiceHandle handle = serviceLocator.getServiceHandle(ad, newInjectee);
@@ -203,10 +203,9 @@ public Class<Context> getAnnotation() {
203203
return Context.class;
204204
}
205205

206-
private final Cache<Injectee, Injectee> foreignRequestScopedInjecteeCache =
207-
new Cache<>(new Computable<Injectee, Injectee>() {
206+
private final Cache<Injectee, Injectee> foreignRequestScopedInjecteeCache = new Cache<>(new Function<Injectee, Injectee>() {
208207
@Override
209-
public Injectee compute(Injectee injectee) {
208+
public Injectee apply(Injectee injectee) {
210209
if (injectee.getParent() != null) {
211210
if (Field.class.isAssignableFrom(injectee.getParent().getClass())) {
212211
Field f = (Field) injectee.getParent();
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
41+
package org.glassfish.jersey.internal.util.collection;
42+
43+
import java.util.concurrent.Callable;
44+
import java.util.concurrent.ConcurrentHashMap;
45+
import java.util.concurrent.ExecutionException;
46+
import java.util.concurrent.Future;
47+
import java.util.concurrent.FutureTask;
48+
import java.util.concurrent.TimeUnit;
49+
import java.util.concurrent.TimeoutException;
50+
import java.util.function.Function;
51+
52+
/**
53+
* Cache implementation that relies on FutureTask.
54+
* Desired value will only be computed once and computed value stored in the cache.
55+
* The implementation is based on an example from the "Java Concurrency in Practice" book
56+
* authored by Brian Goetz and company.
57+
*
58+
* @param <K> The type of the key of the cache
59+
* @param <V> The type of the values in the cache
60+
* @author Jakub Podlesak (jakub.podlesak at oracle.com)
61+
*/
62+
public class Cache<K, V> implements Function<K, V> {
63+
64+
private static final CycleHandler<Object> EMPTY_HANDLER = key -> { };
65+
private final CycleHandler<K> cycleHandler;
66+
private final ConcurrentHashMap<K, OriginThreadAwareFuture> cache = new ConcurrentHashMap<>();
67+
private final Function<K, V> computable;
68+
69+
/**
70+
* Create new cache with given computable to compute values.
71+
* Detected cycles will be ignored as there is a no-op cycle handler registered by default.
72+
*
73+
* @param computable function generated the new value.
74+
*/
75+
@SuppressWarnings("unchecked")
76+
public Cache(Function<K, V> computable) {
77+
this(computable, (CycleHandler<K>) EMPTY_HANDLER);
78+
}
79+
80+
/**
81+
* Create new cache with given computable and cycle handler.
82+
*
83+
* @param computable function generated the new value.
84+
* @param cycleHandler handler used if the thread cycle is met.
85+
*/
86+
public Cache(Function<K, V> computable, CycleHandler<K> cycleHandler) {
87+
this.computable = computable;
88+
this.cycleHandler = cycleHandler;
89+
}
90+
91+
@Override
92+
public V apply(final K key) {
93+
while (true) {
94+
OriginThreadAwareFuture f = cache.get(key);
95+
if (f == null) {
96+
OriginThreadAwareFuture ft = new OriginThreadAwareFuture(key);
97+
98+
f = cache.putIfAbsent(key, ft);
99+
if (f == null) {
100+
f = ft;
101+
ft.run();
102+
}
103+
} else {
104+
final long tid = f.threadId;
105+
if ((tid != -1) && (Thread.currentThread().getId() == f.threadId)) {
106+
cycleHandler.handleCycle(key);
107+
}
108+
}
109+
try {
110+
return f.get();
111+
} catch (InterruptedException ex) {
112+
throw new RuntimeException(ex);
113+
} catch (ExecutionException ex) {
114+
cache.remove(key); // otherwise the exception would be remembered
115+
Throwable cause = ex.getCause();
116+
if (cause == null) {
117+
throw new RuntimeException(ex);
118+
}
119+
if (cause instanceof RuntimeException) {
120+
throw (RuntimeException) cause;
121+
}
122+
throw new RuntimeException(cause);
123+
}
124+
}
125+
}
126+
127+
/**
128+
* Empty cache.
129+
*/
130+
public void clear() {
131+
cache.clear();
132+
}
133+
134+
/**
135+
* Returns true if the key has already been cached.
136+
*
137+
* @param key item key.
138+
* @return true if given key is present in the cache.
139+
*/
140+
public boolean containsKey(final K key) {
141+
return cache.containsKey(key);
142+
}
143+
144+
/**
145+
* Remove item from the cache.
146+
*
147+
* @param key item key.
148+
*/
149+
public void remove(final K key) {
150+
cache.remove(key);
151+
}
152+
153+
/**
154+
* Returns the size of the cache
155+
*
156+
* @return The number of elements in the cache
157+
*/
158+
public int size() {
159+
return cache.size();
160+
}
161+
162+
/**
163+
* Should a cycle be detected during computation of a value
164+
* for given key, this interface allows client code to register
165+
* a callback that would get invoked in such a case.
166+
*
167+
* @param <K> Key type.
168+
*/
169+
public interface CycleHandler<K> {
170+
171+
/**
172+
* Handle cycle that was detected while computing a cache value
173+
* for given key. This method would typically just throw a runtime exception.
174+
*
175+
* @param key instance that caused the cycle.
176+
*/
177+
void handleCycle(K key);
178+
}
179+
180+
/**
181+
* Helper class, that remembers the future task origin thread, so that cycles could be detected.
182+
* If any thread starts computation for given key and the same thread requests the computed value
183+
* before the computation stops, a cycle is detected and registered cycle handler is called.
184+
*/
185+
private class OriginThreadAwareFuture implements Future<V> {
186+
private final FutureTask<V> future;
187+
private volatile long threadId;
188+
189+
OriginThreadAwareFuture(K key) {
190+
this.threadId = Thread.currentThread().getId();
191+
Callable<V> eval = () -> {
192+
try {
193+
return computable.apply(key);
194+
} finally {
195+
threadId = -1;
196+
}
197+
};
198+
this.future = new FutureTask<>(eval);
199+
}
200+
201+
@Override
202+
public int hashCode() {
203+
return future.hashCode();
204+
}
205+
206+
@SuppressWarnings("unchecked")
207+
@Override
208+
public boolean equals(Object obj) {
209+
if (obj == null) {
210+
return false;
211+
}
212+
if (getClass() != obj.getClass()) {
213+
return false;
214+
}
215+
216+
final OriginThreadAwareFuture other = (OriginThreadAwareFuture) obj;
217+
if (this.future != other.future && (this.future == null || !this.future.equals(other.future))) {
218+
return false;
219+
}
220+
return true;
221+
}
222+
223+
@Override
224+
public boolean cancel(boolean mayInterruptIfRunning) {
225+
return future.cancel(mayInterruptIfRunning);
226+
}
227+
228+
@Override
229+
public boolean isCancelled() {
230+
return future.isCancelled();
231+
}
232+
233+
@Override
234+
public boolean isDone() {
235+
return future.isDone();
236+
}
237+
238+
@Override
239+
public V get() throws InterruptedException, ExecutionException {
240+
return future.get();
241+
}
242+
243+
@Override
244+
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
245+
return future.get(timeout, unit);
246+
}
247+
248+
public void run() {
249+
future.run();
250+
}
251+
}
252+
}

core-server/src/main/java/org/glassfish/jersey/server/internal/inject/BeanParamValueSupplierProvider.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,21 @@
4040

4141
package org.glassfish.jersey.server.internal.inject;
4242

43+
import java.util.function.Function;
44+
4345
import javax.ws.rs.BeanParam;
4446

4547
import javax.inject.Provider;
4648
import javax.inject.Singleton;
4749

50+
import org.glassfish.jersey.internal.util.collection.Cache;
4851
import org.glassfish.jersey.process.internal.RequestScoped;
4952
import org.glassfish.jersey.server.ContainerRequest;
5053
import org.glassfish.jersey.server.model.Parameter;
5154
import org.glassfish.jersey.spi.inject.Descriptors;
5255
import org.glassfish.jersey.spi.inject.ForeignDescriptor;
5356
import org.glassfish.jersey.spi.inject.InstanceManager;
5457

55-
import org.glassfish.hk2.utilities.cache.Cache;
56-
import org.glassfish.hk2.utilities.cache.Computable;
57-
5858
/**
5959
* Value factory provider for {@link BeanParam bean parameters}.
6060
*
@@ -70,10 +70,9 @@ private static final class BeanParamValueSupplier extends AbstractRequestDerived
7070
private final InstanceManager instanceManager;
7171

7272
private final Cache<Class<?>, ForeignDescriptor> descriptorCache
73-
= new Cache<>(new Computable<Class<?>, ForeignDescriptor>() {
74-
73+
= new Cache<>(new Function<Class<?>, ForeignDescriptor>() {
7574
@Override
76-
public ForeignDescriptor compute(Class<?> key) {
75+
public ForeignDescriptor apply(Class<?> key) {
7776
// below we make sure HK2 behaves as if injection happens into a request scoped type
7877
// this is to avoid having proxies injected (see JERSEY-2386)
7978
// before touching the following statement, check BeanParamMemoryLeakTest first!
@@ -97,7 +96,7 @@ public Object get() {
9796
if (fromHk2 != null) { // the bean parameter type is already bound in HK2, let's just take it from there
9897
return fromHk2;
9998
}
100-
ForeignDescriptor foreignDescriptor = descriptorCache.compute(rawType);
99+
ForeignDescriptor foreignDescriptor = descriptorCache.apply(rawType);
101100
return instanceManager.getInstance(foreignDescriptor);
102101
}
103102
}

core-server/src/main/java/org/glassfish/jersey/server/internal/inject/DelegatedInjectionValueSupplierProvider.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
import javax.inject.Singleton;
4848

49+
import org.glassfish.jersey.internal.util.collection.Cache;
4950
import org.glassfish.jersey.process.internal.RequestScoped;
5051
import org.glassfish.jersey.server.model.Parameter;
5152
import org.glassfish.jersey.server.model.Parameter.Source;
@@ -57,8 +58,6 @@
5758
import org.glassfish.jersey.spi.inject.Injectee;
5859
import org.glassfish.jersey.spi.inject.InjecteeImpl;
5960

60-
import org.glassfish.hk2.utilities.cache.Cache;
61-
6261
/**
6362
* Value factory provider that delegates the injection target lookup to the underlying injection provider.
6463
*
@@ -110,7 +109,7 @@ private Injectee getInjectee(Parameter parameter) {
110109
InjecteeImpl injectee = new InjecteeImpl();
111110
injectee.setRequiredType(parameter.getType());
112111
injectee.setInjecteeClass(parameter.getRawType());
113-
ForeignDescriptor proxyDescriptor = descriptorCache.compute(parameter);
112+
ForeignDescriptor proxyDescriptor = descriptorCache.apply(parameter);
114113
if (proxyDescriptor != null) {
115114
injectee.setInjecteeDescriptor(proxyDescriptor);
116115
}

0 commit comments

Comments
 (0)