package org.allcolor.services.servlet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import org.allcolor.services.jndi.JNDIPropertiesConfig;
import org.allcolor.services.json.JSONException;
import org.allcolor.services.json.JSONObject;
import org.allcolor.services.json.XML;
import org.allcolor.services.xml.BaseXMLSerializer;
import org.allcolor.services.xml.generators.JSClientServiceGenerator;
import org.allcolor.services.xml.generators.WSDLGenerator;
import org.allcolor.services.xml.generators.WSDLGenerator.MultiOpWsdl;
import org.allcolor.services.xml.rest.Bind;
import org.allcolor.services.xml.rest.HttpMethod;
import org.allcolor.services.xml.rest.ResourceParameter;
import org.allcolor.services.xml.rest.StatusMatch;
import org.allcolor.services.xml.utils.XMLUtils;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public final class ServiceServlet extends HttpServlet {
private static final String ACCEPT_HTTP_HEADER = "Accept";
private static final String ASCII = "ascii";
public static final String FORM_MIME_TYPE = "application/x-www-form-urlencoded";
private static volatile ServiceServlet handle = null;
public static final String JSON_MIME_TYPE = "application/json";
private final static Logger LOG = Logger.getLogger(ServiceServlet.class);
private static final String OVERRIDE_MIMETYPE_OUT = "override.mimetype.out";
private static final long serialVersionUID = 1127003447242933092L;
private static final String SERVICE_CONFIG_FILE = "META-INF/REST-SERVICE.xml";
public static final String SOAP_ACTION_HTTP_HEADER = "SOAPAction";
private static final String SOAP_XMLNS = ServiceServlet.class.getName()
+ ".SOAP_XMLNS";
public static final String TEXT_XML_MIMETYPE = "text/xml";
private static final String UTF8 = "utf-8";
public static final String XML_MIME_TYPE = "application/xml";
public static ServiceServlet getHandle() {
return ServiceServlet.handle;
}
private final Map<String, Method> xmlObjectNewInstanceMap = new Hashtable<String, Method>();
private final Map<String, Method> xmlObjectParseMap = new Hashtable<String, Method>();
@Override
public void destroy() {
super.destroy();
ServiceRegister.getHandle().destroy();
if (ServiceServlet.handle == this) {
ServiceServlet.handle = null;
}
ServiceServlet.LOG.info("Destroyed.");
}
private void execute(final ServiceDescriptor service,
final HttpServletRequest req, final HttpServletResponse resp,
final Method toCall, final Object request, final String output,
final Logger LOG) throws Throwable {
try {
if (LOG.isDebugEnabled()) {
if (request instanceof XmlObject) {
LOG.debug(req.getRequestURI() + " IN:\n"
+ ((XmlObject) request).xmlText());
} else {
LOG.debug(req.getRequestURI() + " IN:\n"
+ BaseXMLSerializer.toXML(request));
}
}
if (toCall == null) {
throw new InstantiationException("Method is null.");
} else if ((toCall.getParameterTypes().length == 4)
&& (toCall.getParameterTypes()[2] == HttpServletRequest.class)
&& (toCall.getParameterTypes()[3] == FileItemList.class)) {
final Object response = this.newInstance(toCall
.getParameterTypes()[1]);
toCall.invoke(service.getService(), new Object[] { request,
response, req,
new FileItemList(RequestWrapper.getFiles()) });
this.prepareResponse(req, resp, output, response, this
.getStatus(toCall, response), LOG);
return;
} else if ((toCall.getParameterTypes().length == 3)
&& (toCall.getParameterTypes()[2] == HttpServletRequest.class)) {
final Object response = this.newInstance(toCall
.getParameterTypes()[1]);
toCall.invoke(service.getService(), new Object[] { request,
response, req });
this.prepareResponse(req, resp, output, response, this
.getStatus(toCall, response), LOG);
return;
} else if ((toCall.getParameterTypes().length == 3)
&& (toCall.getParameterTypes()[1] == HttpServletRequest.class)
&& (toCall.getParameterTypes()[2] == HttpServletResponse.class)) {
toCall.invoke(service.getService(), new Object[] { request,
req, resp });
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType());
}
return;
} else if ((toCall.getParameterTypes().length == 4)
&& (toCall.getParameterTypes()[1] == HttpServletRequest.class)
&& (toCall.getParameterTypes()[2] == HttpServletResponse.class)
&& (toCall.getParameterTypes()[3] == FileItemList.class)) {
toCall.invoke(service.getService(),
new Object[] { request, req, resp,
new FileItemList(RequestWrapper.getFiles()) });
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType());
}
return;
} else if ((toCall.getParameterTypes().length == 3)
&& (toCall.getParameterTypes()[1] == HttpServletResponse.class)
&& (toCall.getParameterTypes()[2] == FileItemList.class)) {
toCall.invoke(service.getService(), new Object[] { request,
resp, new FileItemList(RequestWrapper.getFiles()) });
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType());
}
return;
} else if ((toCall.getParameterTypes().length == 3)
&& (toCall.getParameterTypes()[2] == FileItemList.class)) {
final Object response = this.newInstance(toCall
.getParameterTypes()[1]);
toCall
.invoke(service.getService(), new Object[] { request,
response,
new FileItemList(RequestWrapper.getFiles()) });
this.prepareResponse(req, resp, output, response, this
.getStatus(toCall, response), LOG);
return;
} else if ((toCall.getParameterTypes().length == 2)
&& (toCall.getParameterTypes()[1] == HttpServletResponse.class)) {
toCall.invoke(service.getService(), new Object[] { request,
resp });
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType());
}
return;
} else if (toCall.getParameterTypes().length == 2) {
final Object response = this.newInstance(toCall
.getParameterTypes()[1]);
toCall.invoke(service.getService(), new Object[] { request,
response });
this.prepareResponse(req, resp, output, response, this
.getStatus(toCall, response), LOG);
return;
} else {
throw new IOException("Wrong number of parameters for '"
+ toCall.getName() + "' : "
+ toCall.getParameterTypes().length);
}
} catch (final InvocationTargetException e) {
final Throwable t = e.getTargetException();
if (t instanceof SecurityException) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_UNAUTHORIZED,
t.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", t);
}
return;
}
} else if (t instanceof RemoteException) {
final RemoteException rm = (RemoteException) t;
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp, rm.getErrorCode(),
t.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG
.debug(req.getRequestURI() + " OUT: "
+ rm.getErrorCode() + " - "
+ t.getMessage(), t);
}
return;
}
} else {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t
.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR
+ " - " + t.getMessage(), t);
}
return;
}
}
} catch (final SecurityException e) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp, HttpServletResponse.SC_UNAUTHORIZED,
e.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", e);
}
}
} catch (final RemoteException e) {
if (!resp.isCommitted()) {
resp.reset();
this
.sendError(req, resp, e.getErrorCode(), e.getMessage(),
LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: " + e.getErrorCode()
+ " - " + e.getMessage(), e);
}
return;
}
} catch (final Exception e) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR
+ " - " + e.getMessage(), e);
}
return;
}
}
}
private String getOutput(final HttpServletRequest request, final Logger LOG) {
final String overrideMimeType = request
.getParameter(ServiceServlet.OVERRIDE_MIMETYPE_OUT);
if ((overrideMimeType != null) && !"".equals(overrideMimeType.trim())) {
if (overrideMimeType.equals(ServiceServlet.TEXT_XML_MIMETYPE)) {
return ServiceServlet.XML_MIME_TYPE;
}
return overrideMimeType;
}
final String acceptHeader = request
.getHeader(ServiceServlet.ACCEPT_HTTP_HEADER);
if (LOG.isDebugEnabled()) {
LOG.debug("Accept: " + acceptHeader);
}
if ((acceptHeader != null)
&& (acceptHeader.indexOf(ServiceServlet.JSON_MIME_TYPE) != -1)) {
return ServiceServlet.JSON_MIME_TYPE;
}
if ((acceptHeader != null)
&& (acceptHeader.indexOf(ServiceServlet.FORM_MIME_TYPE) != -1)) {
return ServiceServlet.FORM_MIME_TYPE;
}
return ServiceServlet.XML_MIME_TYPE;
}
private String getQueryString(final Method method,
final String queryString, final String[] urlParameters,
final Logger LOG) {
try {
final StringBuilder buffer = new StringBuilder(
queryString != null ? queryString : "");
final Bind bind = method.getAnnotation(Bind.class);
int index = 0;
for (final ResourceParameter rp : bind.resourceParameters()) {
if (rp.parameterName().trim().length() == 0) {
index++;
continue;
}
buffer.append("&");
buffer.append(URLEncoder.encode(rp.parameterName(),
ServiceServlet.UTF8));
buffer.append("=");
if (!"".equals(rp.urlPrefix())) {
buffer.append(URLEncoder.encode(urlParameters[index]
.substring(rp.urlPrefix().length()),
ServiceServlet.UTF8));
} else {
buffer.append(URLEncoder.encode(urlParameters[index],
ServiceServlet.UTF8));
}
index++;
}
final String result = buffer.toString();
if (LOG.isDebugEnabled()) {
LOG.debug("Querystring: " + result);
}
return result;
} catch (final Exception e) {
LOG.error("Error while adding urlParameters.", e);
}
return queryString;
}
private int getStatus(final Method toCall, final Object response) {
try {
final Bind bind = toCall.getAnnotation(Bind.class);
if ((bind != null) && (bind.statusMatches().length > 0)) {
final Map<String, String[]> map = BaseXMLSerializer
.parseQueryString(BaseXMLSerializer
.toXMLRequestParameters(response));
for (final StatusMatch sm : bind.statusMatches()) {
final String[] values = map.get(sm.parameterName());
if ((values != null) && (values.length > 0)
&& (values[0] != null)
&& values[0].equals(sm.value())) {
return sm.status();
}
}
}
} catch (final Exception ignore) {
}
return HttpServletResponse.SC_OK;
}
private String[] getURLParameters(final String contextPath,
final String requestURI, final String serviceName,
final String servletPath, final Logger LOG) {
try {
if (requestURI.indexOf(contextPath + "/") == -1) {
return new String[0];
}
String uri = requestURI.substring(contextPath.length() + 1);
if (!uri.startsWith("/")) {
uri = "/" + uri;
}
if (uri.startsWith(servletPath)) {
uri = uri.substring(servletPath.length());
}
if (uri.startsWith("/")) {
uri = uri.substring(1);
}
String rest = uri.substring(serviceName.length()).trim();
if (rest.length() == 0) {
return new String[0];
}
rest = URLDecoder.decode(rest, "utf-8").trim();
if (rest.length() == 0) {
return new String[0];
}
final String[] array = rest.split("/");
final List<String> larray = new ArrayList<String>();
final boolean debug = LOG.isDebugEnabled();
for (final String param : array) {
if (param.trim().length() == 0) {
continue;
}
larray.add(param);
if (debug) {
LOG.debug("URL Param: " + param);
}
}
return larray.toArray(new String[larray.size()]);
} catch (final Exception e) {
return new String[0];
}
}
private void handleJSClient(final ServiceDescriptor service,
final HttpServletRequest req, final HttpServletResponse resp,
final String contextPath, final String requestURI)
throws IOException {
try {
final String js = JSClientServiceGenerator.getJSClientService();
resp.setContentType("text/javascript");
final OutputStream out = resp.getOutputStream();
out.write(js.getBytes("utf-8"));
out.flush();
} catch (final Exception e) {
e.printStackTrace();
}
}
private void handleJSDescriptor(final ServiceDescriptor service,
final HttpServletRequest req, final HttpServletResponse resp,
final String contextPath, String requestURI) throws IOException {
try {
if (requestURI.startsWith(contextPath)) {
requestURI = requestURI.substring(contextPath.length());
}
if (requestURI.startsWith("/")) {
requestURI = requestURI.substring(1);
}
final String js = JSClientServiceGenerator.toJSClientService(
service.getServiceClass(), requestURI);
resp.setContentType("text/javascript");
final OutputStream out = resp.getOutputStream();
out.write(js.getBytes("utf-8"));
out.flush();
} catch (final Exception e) {
e.printStackTrace();
}
}
private void handleSOAP(final HttpMethod httpMethod,
final ServiceDescriptor service, final HttpServletRequest req,
final HttpServletResponse resp, final String contextPath,
final String requestURI, final String[] urlParameters,
final Logger LOG) throws IOException {
final Method toCall = service.getMethod(httpMethod, urlParameters, req
.getHeader(ServiceServlet.SOAP_ACTION_HTTP_HEADER));
if (toCall == null) {
LOG.warn(req.getRequestURI() + " - " + requestURI + " "
+ httpMethod.toString() + " not found (404).");
this.sendError(req, resp, HttpServletResponse.SC_NOT_FOUND, service
.getName()
+ " "
+ req.getMethod()
+ " "
+ req.getRequestURI()
+ " not found.", LOG);
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Calling " + toCall.toGenericString() + " on "
+ service.getName());
}
if ((toCall.getParameterTypes().length == 2)
&& (toCall.getParameterTypes()[0] == HttpServletRequest.class)
&& (toCall.getParameterTypes()[1] == HttpServletResponse.class)) {
try {
toCall.invoke(service.getService(), new Object[] { req, resp });
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType());
}
return;
} catch (final InvocationTargetException e) {
final Throwable t = e.getTargetException();
if (t instanceof SecurityException) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_UNAUTHORIZED, t
.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", t);
}
return;
}
}
throw new IOException(t.getMessage());
} catch (final SecurityException e) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_UNAUTHORIZED,
e.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", e);
}
}
return;
} catch (final Exception e) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_BAD_REQUEST, e.getMessage(),
LOG);
}
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ e.getMessage() + " (400).", e);
return;
}
}
Object request = null;
final boolean isXmlObject = this.isRequestXmlObject(toCall);
final String input = this.readInput(req);
try {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.parse(new InputSource(
new StringReader(input)));
NodeList nlBody = doc.getElementsByTagNameNS(
"http://schemas.xmlsoap.org/soap/envelope/", "Body");
req.setAttribute(ServiceServlet.SOAP_XMLNS,
"http://schemas.xmlsoap.org/soap/envelope");
if (nlBody.getLength() == 0) {
nlBody = doc.getElementsByTagNameNS(
"http://www.w3.org/2001/12/soap-envelope", "Body");
if (nlBody.getLength() == 0) {
nlBody = doc.getElementsByTagNameNS(
"http://www.w3.org/2003/05/soap-envelope", "Body");
if (nlBody.getLength() > 0) {
req.setAttribute(ServiceServlet.SOAP_XMLNS,
"http://www.w3.org/2003/05/soap-envelope");
}
} else {
req.setAttribute(ServiceServlet.SOAP_XMLNS,
"http://www.w3.org/2001/12/soap-envelope");
}
}
if (nlBody.getLength() > 0) {
final Element soapBody = (Element) nlBody.item(0);
final NodeList children = soapBody.getChildNodes();
Element rootNode = null;
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
rootNode = (Element) n;
break;
}
}
if (rootNode != null) {
if (isXmlObject) {
request = this.parse(toCall.getParameterTypes()[0],
rootNode);
} else {
request = BaseXMLSerializer.fromXML(rootNode, toCall
.getParameterTypes()[0]);
}
}
}
} catch (final Exception ignore) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ ignore.getMessage() + " (500).", ignore);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ignore
.getMessage(), LOG);
}
return;
}
try {
this.execute(service, req, resp, toCall, request, this.getOutput(
req, LOG), LOG);
} catch (final Throwable e) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ e.getMessage() + " (500).", e);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
.getMessage(), LOG);
}
return;
}
}
private void handleStream(final HttpMethod httpMethod,
final ServiceDescriptor service, final HttpServletRequest req,
final HttpServletResponse resp, final String contextPath,
final String requestURI, final String[] urlParameters,
final Logger LOG) throws IOException {
final Method toCall = service.getMethod(httpMethod, urlParameters);
if (toCall == null) {
LOG.warn(req.getRequestURI() + " - " + requestURI + " "
+ httpMethod.toString() + " not found (404).");
this.sendError(req, resp, HttpServletResponse.SC_NOT_FOUND, service
.getName()
+ " "
+ req.getMethod()
+ " "
+ req.getRequestURI()
+ " not found.", LOG);
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Calling " + toCall.toGenericString() + " on "
+ service.getName());
}
if ((toCall.getParameterTypes().length == 2)
&& (toCall.getParameterTypes()[0] == HttpServletRequest.class)
&& (toCall.getParameterTypes()[1] == HttpServletResponse.class)) {
try {
toCall.invoke(service.getService(), new Object[] { req, resp });
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType());
}
return;
} catch (final InvocationTargetException e) {
final Throwable t = e.getTargetException();
if (t instanceof SecurityException) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_UNAUTHORIZED, t
.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", t);
}
return;
}
}
throw new IOException(t.getMessage());
} catch (final SecurityException e) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_UNAUTHORIZED,
e.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", e);
}
}
return;
} catch (final Exception e) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ e.getMessage() + " (400).", e);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_BAD_REQUEST, e.getMessage(),
LOG);
}
return;
}
}
Object request = null;
final boolean isXmlObject = this.isRequestXmlObject(toCall);
final String input = this.readInput(req);
LOG.debug("Received : "+input+" - "+req.getContentType());
try {
if ("".equals(input)
&& ((req.getQueryString() == null) || "".equals(req
.getQueryString().trim()))) {
if (isXmlObject) {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.newDocument();
BaseXMLSerializer.fromXMLToXML(doc, BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
request = this.parse(toCall.getParameterTypes()[0], doc);
} else {
request = BaseXMLSerializer.fromXML(BaseXMLSerializer
.toXMLDOM(toCall.getParameterTypes()[0]
.newInstance()), BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
}
} else if (((req.getContentType() == null) && !req.getMethod()
.equalsIgnoreCase("POST"))
|| ("".equals(req.getContentType()) && !req.getMethod()
.equalsIgnoreCase("POST"))
|| ServiceServlet.XML_MIME_TYPE
.equals(req.getContentType())
|| ServiceServlet.TEXT_XML_MIMETYPE
.equals(req.getContentType())) {
if (isXmlObject) {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.parse(new InputSource(
new StringReader(input)));
BaseXMLSerializer.fromXMLToXML(doc, BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
request = this.parse(toCall.getParameterTypes()[0], doc);
} else {
request = BaseXMLSerializer.fromXML(BaseXMLSerializer
.toXMLDOM(BaseXMLSerializer.fromXML(input, toCall
.getParameterTypes()[0])),
BaseXMLSerializer.parseQueryString(this
.getQueryString(toCall, req
.getQueryString(), urlParameters,
LOG)),
toCall.getParameterTypes()[0]);
}
} else if (ServiceServlet.JSON_MIME_TYPE.equals(req
.getContentType())) {
if (isXmlObject) {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.parse(new InputSource(
new StringReader(XML
.toString(new JSONObject(input)))));
BaseXMLSerializer.fromXMLToXML(doc, BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
request = this.parse(toCall.getParameterTypes()[0], doc);
} else {
request = BaseXMLSerializer.fromXML(BaseXMLSerializer
.toXMLDOM(BaseXMLSerializer.fromJSON(input, toCall
.getParameterTypes()[0])),
BaseXMLSerializer.parseQueryString(this
.getQueryString(toCall, req
.getQueryString(), urlParameters,
LOG)),
toCall.getParameterTypes()[0]);
}
} else if (((req.getContentType() == null) && req.getMethod()
.equalsIgnoreCase("POST"))
|| ServiceServlet.FORM_MIME_TYPE.equals(req
.getContentType())) {
if (isXmlObject) {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.newDocument();
BaseXMLSerializer.fromXMLToXML(doc, BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, input
+ "&" + req.getQueryString(),
urlParameters, LOG)), toCall
.getParameterTypes()[0]);
request = this.parse(toCall.getParameterTypes()[0], doc);
} else {
request = BaseXMLSerializer
.fromXMLRequestParameter(this.getQueryString(
toCall, input + "&" + req.getQueryString(),
urlParameters, LOG), toCall
.getParameterTypes()[0]);
}
}
if (request == null) {
if (isXmlObject) {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.newDocument();
BaseXMLSerializer.fromXMLToXML(doc, BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
request = this.parse(toCall.getParameterTypes()[0], doc);
} else {
request = BaseXMLSerializer.fromXML(BaseXMLSerializer
.toXMLDOM(toCall.getParameterTypes()[0]
.newInstance()), BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
}
}
} catch (final Exception ignore) {
try {
if (isXmlObject) {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.newDocument();
BaseXMLSerializer.fromXMLToXML(doc, BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
request = this.parse(toCall.getParameterTypes()[0], doc);
} else {
request = BaseXMLSerializer.fromXML(BaseXMLSerializer
.toXMLDOM(toCall.getParameterTypes()[0]
.newInstance()), BaseXMLSerializer
.parseQueryString(this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG)),
toCall.getParameterTypes()[0]);
}
} catch (final Exception hmmm) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ hmmm.getMessage() + " (500).", hmmm);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, hmmm
.getMessage(), LOG);
}
return;
}
}
try {
this.execute(service, req, resp, toCall, request, this.getOutput(
req, LOG), LOG);
} catch (final Throwable e) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ e.getMessage() + " (400).", e);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp, HttpServletResponse.SC_BAD_REQUEST, e
.getMessage(), LOG);
}
return;
}
}
private void handleUrl(final ServiceDescriptor service,
final HttpServletRequest req, final HttpServletResponse resp,
final String contextPath, final String requestURI,
final String[] urlParameters, final Logger LOG) throws IOException {
final Method toCall = service.getMethod(HttpMethod.GET, urlParameters);
if (toCall == null) {
LOG.warn(req.getRequestURI() + " - " + requestURI
+ " GET not found (404).");
this.sendError(req, resp, HttpServletResponse.SC_NOT_FOUND, service
.getName()
+ " "
+ req.getMethod()
+ " "
+ req.getRequestURI()
+ " not found.", LOG);
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Calling " + toCall.toGenericString() + " on "
+ service.getName());
}
if ((toCall.getParameterTypes().length == 2)
&& (toCall.getParameterTypes()[0] == HttpServletRequest.class)
&& (toCall.getParameterTypes()[1] == HttpServletResponse.class)) {
try {
toCall.invoke(service.getService(), new Object[] { req, resp });
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType());
return;
} catch (final InvocationTargetException e) {
final Throwable t = e.getTargetException();
if (t instanceof SecurityException) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_UNAUTHORIZED, t
.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", t);
}
return;
}
}
throw new IOException(t.getMessage());
} catch (final SecurityException e) {
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_UNAUTHORIZED,
e.getMessage(), LOG);
if (LOG.isDebugEnabled()) {
LOG.debug(service.getName()
+ " OUT: 401 - need authentication.", e);
}
}
return;
} catch (final Exception e) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ e.getMessage() + " (400).", e);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_BAD_REQUEST, e.getMessage(),
LOG);
}
return;
}
}
Object request = null;
final boolean isXmlObject = this.isRequestXmlObject(toCall);
try {
final String queryString = this.getQueryString(toCall, req
.getQueryString(), urlParameters, LOG);
if ((queryString != null) && !"".equals(queryString.trim())) {
if (isXmlObject) {
final DocumentBuilder p = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = p.newDocument();
BaseXMLSerializer.fromXMLToXML(doc, BaseXMLSerializer
.parseQueryString(queryString), toCall
.getParameterTypes()[0]);
request = this.parse(toCall.getParameterTypes()[0], doc);
} else {
request = BaseXMLSerializer.fromXMLRequestParameter(
queryString, toCall.getParameterTypes()[0]);
}
}
if (request == null) {
if (isXmlObject) {
request = this.newInstance(toCall.getParameterTypes()[0]);
} else {
request = toCall.getParameterTypes()[0].newInstance();
}
}
} catch (final Exception ignore) {
try {
if (isXmlObject) {
request = this.newInstance(toCall.getParameterTypes()[0]);
} else {
request = toCall.getParameterTypes()[0].newInstance();
}
} catch (final Exception hmmm) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ hmmm.getMessage() + " (500).", hmmm);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, hmmm
.getMessage(), LOG);
}
return;
}
}
try {
this.execute(service, req, resp, toCall, request, this.getOutput(
req, LOG), LOG);
} catch (final Throwable e) {
LOG.error(req.getRequestURI() + " - " + requestURI + " - "
+ e.getMessage() + " (500).", e);
if (!resp.isCommitted()) {
resp.reset();
this.sendError(req, resp,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e
.getMessage(), LOG);
}
return;
}
}
private void handleWSDL(final ServiceDescriptor service,
final HttpServletRequest req, final HttpServletResponse resp,
final String contextPath, final String requestURI)
throws IOException {
try {
final Method[] methods = service.getServiceClass()
.getDeclaredMethods();
final List<MultiOpWsdl> listOp = new ArrayList<MultiOpWsdl>();
for (final Method m : methods) {
final Bind b = m.getAnnotation(Bind.class);
if (b != null) {
final Class<?>[] parameters = m.getParameterTypes();
final MultiOpWsdl mow = new MultiOpWsdl(parameters[0],
parameters[1], m.getName(), null);
listOp.add(mow);
}
}
final String wsdl = WSDLGenerator.toWSDL(service.getName(), req
.getScheme()
+ "://" + req.getHeader("Host") + req.getRequestURI(),
listOp);
resp.setContentType(ServiceServlet.XML_MIME_TYPE);
final OutputStream out = resp.getOutputStream();
out.write(wsdl.getBytes("utf-8"));
out.flush();
} catch (final Exception e) {
e.printStackTrace();
}
}
@Override
public void init(final ServletConfig config) throws ServletException {
super.init(config);
ServiceServlet.LOG.info("Starting.");
try {
final Enumeration<URL> manifests = Thread.currentThread()
.getContextClassLoader().getResources(
ServiceServlet.SERVICE_CONFIG_FILE);
for (; manifests.hasMoreElements();) {
InputStream in = null;
try {
in = manifests.nextElement().openStream();
final ServiceXmlList sxmls = BaseXMLSerializer.fromXML(in,
ServiceXmlList.class);
if ((sxmls != null) && (sxmls.getList() != null)) {
synchronized (ServiceRegister.getHandle().getServicesMap()) {
for (final ServiceXml sxml : sxmls.getList()) {
ServiceRegister.getHandle().getServicesMap().put(sxml.getName(), sxml);
ServiceServlet.LOG
.info("Service " + sxml.getName()
+ " (" + sxml.getClassName()
+ ") registered.");
}
}
}
} catch (final Exception ignore) {
ServiceServlet.LOG.error("Error creating service.", ignore);
} finally {
try {
in.close();
} catch (final Exception ignore) {
}
}
}
} catch (final Exception e) {
ServiceServlet.LOG.error("Error while starting.", e);
}
try {
final Context initCtx = new InitialContext();
final Context envCtx = (Context) initCtx.lookup("java:comp/env");
final JNDIPropertiesConfig jndiProps = (JNDIPropertiesConfig) envCtx
.lookup("alcrest/serviceslist");
final ServiceXmlList sxmls = new ServiceXmlList();
sxmls.setServicesProperties(jndiProps.getContentAsProperties());
if ((sxmls != null) && (sxmls.getList() != null)) {
synchronized (ServiceRegister.getHandle().getServicesMap()) {
for (final ServiceXml sxml : sxmls.getList()) {
ServiceRegister.getHandle().getServicesMap().put(sxml.getName(), sxml);
ServiceServlet.LOG.info("Service " + sxml.getName()
+ " (" + sxml.getClassName() + ") registered.");
}
}
}
} catch (final Exception e) {
ServiceServlet.LOG
.warn("no JNDIPropertiesConfig found. (java:comp/env/alcrest/serviceslist)");
}
ServiceServlet.handle = this;
ServiceServlet.LOG.info("Started.");
}
private boolean isRequestXmlObject(final Method toCall) {
final Class<?>[] parameters = toCall.getParameterTypes();
return (parameters != null) && (parameters.length >= 1)
&& XmlObject.class.isAssignableFrom(parameters[0]);
}
private Object newInstance(final Class<?> clazz) throws Exception {
if (XmlObject.class.isAssignableFrom(clazz)) {
Method newInstance = this.xmlObjectNewInstanceMap.get(clazz
.getName());
if (newInstance == null) {
final Class<?> factory = clazz.getClassLoader().loadClass(
clazz.getName() + "$Factory");
newInstance = factory.getMethod("newInstance", new Class<?>[0]);
this.xmlObjectNewInstanceMap.put(clazz.getName(), newInstance);
}
return newInstance.invoke(null, new Object[0]);
} else {
return clazz.newInstance();
}
}
private XmlObject parse(final Class<?> clazz, final Node node)
throws Exception {
Method parse = this.xmlObjectParseMap.get(clazz.getName());
if (parse == null) {
final Class<?> factory = clazz.getClassLoader().loadClass(
clazz.getName() + "$Factory");
parse = factory.getMethod("parse", new Class<?>[] { Node.class });
this.xmlObjectParseMap.put(clazz.getName(), parse);
}
final XmlObject ret = (XmlObject) parse.invoke(null,
new Object[] { node });
return ret;
}
private void prepareResponse(final HttpServletRequest req,
final HttpServletResponse resp, final String output,
final Object response, final int status, final Logger LOG)
throws UnsupportedEncodingException, IOException {
resp.setContentType(output);
resp.setStatus(status);
if (req.getHeader(ServiceServlet.SOAP_ACTION_HTTP_HEADER) != null) {
resp.setCharacterEncoding(ServiceServlet.UTF8);
final String xml = "<soap:Envelope xmlns:soap=\""
+ req.getAttribute(ServiceServlet.SOAP_XMLNS)
+ "/\" >\n"
+ "<soap:Body>\n"
+ (response instanceof XmlObject ? this
.serializeToXml(response) : BaseXMLSerializer
.toXML(response)) + "\n</soap:Body>"
+ "\n</soap:Envelope>";
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType() + "\n" + xml);
}
final byte[] array = xml.getBytes(ServiceServlet.UTF8);
resp.setContentLength(array.length);
final OutputStream rout = resp.getOutputStream();
rout.write(array);
rout.flush();
return;
} else if (ServiceServlet.XML_MIME_TYPE.equals(output)) {
resp.setCharacterEncoding(ServiceServlet.UTF8);
final String xml = response instanceof XmlObject ? this
.serializeToXml(response) : BaseXMLSerializer
.toXML(response);
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType() + "\n" + xml);
}
final byte[] array = xml.getBytes(ServiceServlet.UTF8);
resp.setContentLength(array.length);
final OutputStream rout = resp.getOutputStream();
rout.write(array);
rout.flush();
return;
} else if (ServiceServlet.FORM_MIME_TYPE.equals(output)) {
resp.setCharacterEncoding(ServiceServlet.ASCII);
final String formUrlEncoded = response instanceof XmlObject ? this
.serializeToFormUrlEncoded(response) : BaseXMLSerializer
.toXMLRequestParameters(response);
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType() + "\n" + formUrlEncoded);
}
final byte[] array = formUrlEncoded.getBytes(ServiceServlet.ASCII);
resp.setContentLength(array.length);
final OutputStream rout = resp.getOutputStream();
rout.write(array);
rout.flush();
return;
} else if (ServiceServlet.JSON_MIME_TYPE.equals(output)) {
resp.setCharacterEncoding(ServiceServlet.UTF8);
final String json = response instanceof XmlObject ? this
.serializeToJson(response) : BaseXMLSerializer
.toJSON(response);
if (LOG.isDebugEnabled()) {
LOG.debug(req.getRequestURI() + " OUT: "
+ resp.getContentType() + "\n" + json);
}
final byte[] array = json.getBytes(ServiceServlet.UTF8);
resp.setContentLength(array.length);
final OutputStream rout = resp.getOutputStream();
rout.write(array);
rout.flush();
return;
} else {
LOG.error(req.getRequestURI() + " - " + output
+ " Method Not Allowed.");
this.sendError(req, resp,
HttpServletResponse.SC_METHOD_NOT_ALLOWED, req.getMethod()
+ " response mimetype: " + output, LOG);
return;
}
}
private String readInput(final HttpServletRequest req) {
try {
final StringWriter buffer = new StringWriter();
final BufferedReader reader = new BufferedReader(
new InputStreamReader(req.getInputStream(),
ServiceServlet.UTF8));
final char[] array = new char[2048];
int inb = -1;
while ((inb = reader.read(array)) != -1) {
buffer.write(array, 0, inb);
}
reader.close();
return buffer.toString().trim();
} catch (final Exception e) {
return "";
}
}
private void sendError(final HttpServletRequest req,
final HttpServletResponse resp, final int code,
final String message, final Logger LOG) throws IOException {
if (req.getHeader(ServiceServlet.SOAP_ACTION_HTTP_HEADER) != null) {
this.sendSOAPFault(req, resp, code, message);
} else {
this.prepareResponse(req, resp, this.getOutput(req, LOG),
new ErrorObject(message), code, LOG);
}
}
private void sendSOAPFault(final HttpServletRequest req,
final HttpServletResponse resp, final int code, final String message)
throws IOException {
resp.setContentType(ServiceServlet.XML_MIME_TYPE);
resp.setCharacterEncoding(ServiceServlet.UTF8);
if (req.getAttribute(ServiceServlet.SOAP_XMLNS) == null) {
req.setAttribute(ServiceServlet.SOAP_XMLNS,
"http://schemas.xmlsoap.org/soap/envelope");
}
final String xml = "<soap:Envelope xmlns:soap=\""
+ req.getAttribute(ServiceServlet.SOAP_XMLNS) + "/\">\n"
+ "\t<soap:Body>\n\t\t<soap:Fault>\n"
+ "\t\t\t<faultcode>soap:Server</faultcode>\n"
+ "\t\t\t<faultstring>"
+ XMLUtils.getInstance().escape(message) + "</faultstring>\n"
+ "\t\t\t<faultactor>" + req.getScheme() + "://"
+ req.getHeader("Host") + req.getContextPath() + "/{"
+ req.getHeader("SOAPAction") + "}(" + code + ")"
+ "</faultactor>\n" + "\t\t</soap:Fault>\n\t</soap:Body>"
+ "\n</soap:Envelope>";
final byte[] array = xml.getBytes(ServiceServlet.UTF8);
resp.setContentLength(array.length);
final OutputStream rout = resp.getOutputStream();
rout.write(array);
rout.flush();
}
private String serializeToFormUrlEncoded(final Object response) {
try {
final DocumentBuilder builder = BaseXMLSerializer.factory
.newDocumentBuilder();
final Document doc = builder.parse(new InputSource(
new StringReader(this.serializeToXml(response))));
return BaseXMLSerializer.toXMLRequestParameters(doc);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private String serializeToJson(final Object response) {
try {
final String xml = XMLUtils.getInstance().rewriteXml(
this.serializeToXml(response), new String[0]);
return XML.toJSONObject(xml).toString();
} catch (final JSONException e) {
throw new RuntimeException(e);
}
}
private String serializeToXml(final Object response) {
try {
final Method xmlText = response.getClass().getMethod("xmlText",
new Class[0]);
return (String) xmlText.invoke(response, new Object[0]);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void service(final HttpServletRequest req,
final HttpServletResponse resp) throws ServletException,
IOException {
if (ServiceServlet.handle == null) {
ServiceServlet.handle = this;
}
req.setCharacterEncoding(ServiceServlet.UTF8);
final String contextPath = req.getContextPath();
final String requestURI = req.getRequestURI();
final String servletPath = req.getServletPath();
final String from = req.getRemoteAddr();
final long start = System.currentTimeMillis();
ServiceServlet.LOG.info("Servicing: " + requestURI + " from " + from);
try {
final ServiceDescriptor service = ServiceRegister.getHandle().getService(contextPath,
requestURI, servletPath);
if (service == null) {
if (req.getMethod().equals(HttpMethod.GET.toString())
&& "true".equals(req.getParameter("__jsclient"))) {
this.handleJSClient(service, req, resp, contextPath,
requestURI);
} else {
if (ServiceServlet.LOG.isDebugEnabled()) {
ServiceServlet.LOG.debug("Service not found: "
+ requestURI + " from " + from);
}
this.sendError(req, resp, HttpServletResponse.SC_NOT_FOUND,
requestURI + " not found.", ServiceServlet.LOG);
}
return;
}
final Logger LOG = Logger.getLogger("SERVICE." + service.getName());
if (LOG.isDebugEnabled()) {
LOG.debug("Service found: " + requestURI + " - "
+ service.getName() + " (" + service.getServiceClass()
+ ")" + " from " + from);
}
final String[] urlParameters = this.getURLParameters(contextPath,
requestURI, service.getName(), servletPath, LOG);
if (req.getMethod().equals(HttpMethod.GET.toString())) {
if ("true".equals(req.getParameter("__wsdl"))) {
this
.handleWSDL(service, req, resp, contextPath,
requestURI);
} else if ("true".equals(req.getParameter("__jsdescriptor"))) {
this.handleJSDescriptor(service, req, resp, contextPath,
requestURI);
} else {
this.handleUrl(service, req, resp, contextPath, requestURI,
urlParameters, LOG);
}
} else if (req.getMethod().equals(HttpMethod.PUT.toString())) {
this.handleStream(HttpMethod.PUT, service, req, resp,
contextPath, requestURI, urlParameters, LOG);
} else if (req.getMethod().equals(HttpMethod.POST.toString())) {
if (req.getHeader(ServiceServlet.SOAP_ACTION_HTTP_HEADER) != null) {
this.handleSOAP(HttpMethod.POST, service, req, resp,
contextPath, requestURI, urlParameters, LOG);
} else {
this.handleStream(HttpMethod.POST, service, req, resp,
contextPath, requestURI, urlParameters, LOG);
}
} else if (req.getMethod().equals(HttpMethod.DELETE.toString())) {
this.handleStream(HttpMethod.DELETE, service, req, resp,
contextPath, requestURI, urlParameters, LOG);
} else {
LOG.error("Error while servicing: " + requestURI
+ " , invalid method: " + req.getMethod());
}
} catch (final Exception e) {
ServiceServlet.LOG.error("Error while servicing: " + requestURI, e);
throw new ServletException(e);
} finally {
final long end = System.currentTimeMillis();
ServiceServlet.LOG.info("Finished Servicing: " + requestURI
+ " from " + from + " time: " + (end - start) + "ms");
}
}
}
|