blob: be7d72bbce1dfc2198dbe83b6a374d1f704d817d [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.cxf.jaxrs.client;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
/**
* Keeps the client state such as the baseURI, currentURI, requestHeaders, current response
* in a thread local storage
*
*/
public class ThreadLocalClientState implements ClientState {
private Map<Thread, LocalClientState> state =
Collections.synchronizedMap(new WeakHashMap<Thread, LocalClientState>());
private LocalClientState initialState;
private Map<Thread, Long> checkpointMap;
private long timeToKeepState;
public ThreadLocalClientState(String baseURI) {
this(baseURI, Collections.<String, Object>emptyMap());
}
public ThreadLocalClientState(String baseURI, Map<String, Object> properties) {
this.initialState = new LocalClientState(URI.create(baseURI), properties);
}
public ThreadLocalClientState(String baseURI, long timeToKeepState) {
this(baseURI, timeToKeepState, Collections.<String, Object>emptyMap());
}
public ThreadLocalClientState(String baseURI, long timeToKeepState, Map<String, Object> properties) {
this.initialState = new LocalClientState(URI.create(baseURI), properties);
this.timeToKeepState = timeToKeepState;
}
public ThreadLocalClientState(LocalClientState initialState, long timeToKeepState) {
this.initialState = initialState;
this.timeToKeepState = timeToKeepState;
}
public void setCurrentBuilder(UriBuilder currentBuilder) {
getState().setCurrentBuilder(currentBuilder);
}
public UriBuilder getCurrentBuilder() {
return getState().getCurrentBuilder();
}
public void setBaseURI(URI baseURI) {
getState().setBaseURI(baseURI);
}
public URI getBaseURI() {
return getState().getBaseURI();
}
public void setResponse(Response response) {
getState().setResponse(response);
}
public Response getResponse() {
return getState().getResponse();
}
public void setRequestHeaders(MultivaluedMap<String, String> requestHeaders) {
getState().setRequestHeaders(requestHeaders);
}
public MultivaluedMap<String, String> getRequestHeaders() {
return getState().getRequestHeaders();
}
public MultivaluedMap<String, String> getTemplates() {
return getState().getTemplates();
}
public void setTemplates(MultivaluedMap<String, String> map) {
getState().setTemplates(map);
}
public void reset() {
removeThreadLocalState(Thread.currentThread());
}
public ClientState newState(URI currentURI,
MultivaluedMap<String, String> headers,
MultivaluedMap<String, String> templates) {
LocalClientState ls = (LocalClientState)getState().newState(currentURI, headers, templates);
return new ThreadLocalClientState(ls, timeToKeepState);
}
@Override
public ClientState newState(URI currentURI,
MultivaluedMap<String, String> headers,
MultivaluedMap<String, String> templates,
Map<String, Object> properties) {
LocalClientState ls = (LocalClientState)getState().newState(currentURI, headers, templates, properties);
return new ThreadLocalClientState(ls, timeToKeepState);
}
private void removeThreadLocalState(Thread t) {
state.remove(t);
if (checkpointMap != null) {
checkpointMap.remove(t);
}
}
protected ClientState getState() {
LocalClientState cs = state.get(Thread.currentThread());
if (cs == null) {
cs = new LocalClientState(initialState);
state.put(Thread.currentThread(), cs);
if (timeToKeepState > 0) {
prepareCheckpointMap();
long currentTime = System.currentTimeMillis();
checkpointMap.put(Thread.currentThread(), currentTime);
Thread clThread = new CleanupThread(Thread.currentThread(), currentTime);
clThread.setName("Client state cleanup thread " + clThread.hashCode());
clThread.start();
}
}
return cs;
}
public void setTimeToKeepState(long timeToKeepState) {
this.timeToKeepState = timeToKeepState;
if (timeToKeepState > 0) {
prepareCheckpointMap();
}
}
private void prepareCheckpointMap() {
if (checkpointMap == null) {
checkpointMap = new ConcurrentHashMap<Thread, Long>();
}
}
private class CleanupThread extends Thread {
private Thread thread;
private long originalTime;
CleanupThread(Thread thread, long originalTime) {
this.thread = thread;
this.originalTime = originalTime;
}
@Override
public void run() {
try {
Thread.sleep(timeToKeepState);
long actualTime = checkpointMap.get(thread);
// if times do not match then the original worker thread
// has called reset() but came back again to create new local state
// hence there's another cleanup thread nearby which will clean the state
if (actualTime == originalTime) {
removeThreadLocalState(thread);
}
} catch (InterruptedException ex) {
// ignore
}
}
}
}