com.sun.xml.ws.server.StatefulInstanceResolver Maven / Gradle / Ivy
package com.sun.xml.ws.server;
import com.sun.istack.FragmentContentHandler;
import com.sun.istack.NotNull;
import com.sun.istack.Nullable;
import com.sun.xml.stream.buffer.XMLStreamBufferSource;
import com.sun.xml.stream.buffer.stax.StreamWriterBufferCreator;
import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.server.InstanceResolver;
import com.sun.xml.ws.api.server.WSEndpoint;
import com.sun.xml.ws.api.server.WSWebServiceContext;
import com.sun.xml.ws.developer.EPRRecipe;
import com.sun.xml.ws.developer.StatefulWebServiceManager;
import com.sun.xml.ws.resources.ServerMessages;
import com.sun.xml.ws.spi.ProviderImpl;
import com.sun.xml.ws.util.xml.ContentHandlerToXMLStreamWriter;
import com.sun.xml.ws.util.xml.XmlUtil;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXResult;
import javax.xml.ws.EndpointReference;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* {@link InstanceResolver} that looks at JAX-WS cookie header to
* determine the instance to which a message will be routed.
*
*
* See {@link StatefulWebServiceManager} for more about user-level semantics.
*
* @author Kohsuke Kawaguchi
*/
public final class StatefulInstanceResolver extends AbstractMultiInstanceResolver implements StatefulWebServiceManager {
/**
* This instance is used for serving messages that have no cookie
* or cookie value that the server doesn't recognize.
*/
private volatile @Nullable T fallback;
/**
* Maintains the stateful service instance and its time-out timer.
*/
private final class Instance {
final @NotNull T instance;
TimerTask task;
public Instance(T instance) {
this.instance = instance;
}
/**
* Resets the timer.
*/
public synchronized void restartTimer() {
cancel();
if(timeoutMilliseconds==0) return; // no timer
task = new TimerTask() {
public void run() {
try {
Callback cb = timeoutCallback;
if(cb!=null) {
cb.onTimeout(instance,StatefulInstanceResolver.this);
return;
}
// default operation is to unexport it.
unexport(instance);
} catch (Throwable e) {
// don't let an error in the code kill the timer thread
logger.log(Level.SEVERE, "time out handler failed", e);
}
}
};
timer.schedule(task,timeoutMilliseconds);
}
/**
* Cancels the timer.
*/
public synchronized void cancel() {
if(task!=null)
task.cancel();
task = null;
}
}
/**
* Maps object ID to instances.
*/
private final Map instances = Collections.synchronizedMap(new HashMap());
/**
* Reverse look up for {@link #instances}.
*/
private final Map reverseInstances = Collections.synchronizedMap(new HashMap());
// time out control. 0=disabled
private volatile long timeoutMilliseconds = 0;
private volatile Callback timeoutCallback;
public StatefulInstanceResolver(Class clazz) {
super(clazz);
}
@Override
public @NotNull T resolve(Packet request) {
HeaderList headers = request.getMessage().getHeaders();
Header header = headers.get(COOKIE_TAG, true);
String id=null;
if(header!=null) {
// find the instance
id = header.getStringContent();
Instance o = instances.get(id);
if(o!=null) {
o.restartTimer();
return o.instance;
}
// huh? what is this ID?
logger.log(Level.INFO,"Request had an unrecognized object ID "+id);
}
// need to fallback
T fallback = this.fallback;
if(fallback!=null)
return fallback;
if(id==null)
throw new WebServiceException(ServerMessages.STATEFUL_COOKIE_HEADER_REQUIRED(COOKIE_TAG));
else
throw new WebServiceException(ServerMessages.STATEFUL_COOKIE_HEADER_INCORRECT(COOKIE_TAG,id));
}
@Override
public void start(WSWebServiceContext wsc, WSEndpoint endpoint) {
super.start(wsc,endpoint);
if(endpoint.getBinding().getAddressingVersion()==null)
// addressing is not enabled.
throw new WebServiceException(ServerMessages.STATEFUL_REQURES_ADDRESSING(clazz));
// inject StatefulWebServiceManager.
for(Field field: clazz.getDeclaredFields()) {
if(field.getType()==StatefulWebServiceManager.class) {
if(!Modifier.isStatic(field.getModifiers()))
throw new WebServiceException(ServerMessages.STATIC_RESOURCE_INJECTION_ONLY(StatefulWebServiceManager.class,field));
new FieldInjectionPlan(field).inject(null,this);
}
}
for(Method method : clazz.getDeclaredMethods()) {
Class[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1)
continue; // not what we are looking for
if(paramTypes[0]==StatefulWebServiceManager.class) {
if(!Modifier.isStatic(method.getModifiers()))
throw new WebServiceException(ServerMessages.STATIC_RESOURCE_INJECTION_ONLY(StatefulWebServiceManager.class,method));
new MethodInjectionPlan(method).inject(null,this);
}
}
}
@Override
public void dispose() {
reverseInstances.clear();
synchronized(instances) {
for (Instance t : instances.values()) {
t.cancel();
dispose(t.instance);
}
instances.clear();
}
if(fallback!=null)
dispose(fallback);
fallback = null;
}
@NotNull
public W3CEndpointReference export(T o) {
return export(W3CEndpointReference.class,o);
}
@NotNull
public EPR export(Class epr, T o) {
return export(epr,o,null);
}
public EPR export(Class epr, T o, EPRRecipe recipe) {
return export(epr, InvokerTube.getCurrentPacket(), o, recipe);
}
@NotNull
public EPR export(Class epr, WebServiceContext context, T o) {
if (context instanceof WSWebServiceContext) {
WSWebServiceContext wswsc = (WSWebServiceContext) context;
return export(epr, wswsc.getRequestPacket(), o);
}
throw new WebServiceException(ServerMessages.STATEFUL_INVALID_WEBSERVICE_CONTEXT(context));
}
@NotNull
public EPR export(Class adrsVer, @NotNull Packet currentRequest, T o) {
return export(adrsVer,currentRequest,o,null);
}
public EPR export(Class adrsVer, @NotNull Packet currentRequest, T o, EPRRecipe recipe) {
return export(adrsVer, currentRequest.webServiceContextDelegate.getEPRAddress(currentRequest,owner), o, recipe);
}
@NotNull
public EPR export(Class adrsVer, String endpointAddress, T o) {
return export(adrsVer,endpointAddress,o,null);
}
@NotNull
public EPR export(Class adrsVer, String endpointAddress, T o, EPRRecipe recipe) {
if(endpointAddress==null)
throw new IllegalArgumentException("No address available");
String key = reverseInstances.get(o);
if(key!=null) return createEPR(key, adrsVer, endpointAddress, recipe);
// not exported yet.
synchronized(this) {
// double check now in the synchronization block to
// really make sure that we can export.
key = reverseInstances.get(o);
if(key!=null) return createEPR(key, adrsVer, endpointAddress, recipe);
if(o!=null)
prepare(o);
key = UUID.randomUUID().toString();
Instance instance = new Instance(o);
instances.put(key, instance);
reverseInstances.put(o,key);
if(timeoutMilliseconds!=0)
instance.restartTimer();
}
return createEPR(key, adrsVer, endpointAddress, recipe);
}
/**
* Creates an EPR that has the right key.
*/
private EPR createEPR(String key, Class eprClass, String address, EPRRecipe recipe) {
AddressingVersion adrsVer = AddressingVersion.fromSpecClass(eprClass);
try {
StreamWriterBufferCreator w = new StreamWriterBufferCreator();
w.writeStartDocument();
w.writeStartElement("wsa","EndpointReference", adrsVer.nsUri);
w.writeNamespace("wsa",adrsVer.nsUri);
w.writeStartElement("wsa","Address",adrsVer.nsUri);
w.writeCharacters(address);
w.writeEndElement();
w.writeStartElement("wsa","ReferenceParameters",adrsVer.nsUri);
w.writeStartElement(COOKIE_TAG.getPrefix(), COOKIE_TAG.getLocalPart(), COOKIE_TAG.getNamespaceURI());
w.writeCharacters(key);
w.writeEndElement();
if(recipe!=null) {
for (Header h : recipe.getReferenceParameters())
h.writeTo(w);
}
w.writeEndElement();
if(recipe!=null) {
List