001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: */
019:
020: package de.schlund.pfixcore.webservice.jsonws;
021:
022: import java.io.IOException;
023: import java.io.StringReader;
024: import java.io.Writer;
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.Type;
028: import java.net.URL;
029: import java.util.Iterator;
030: import java.util.List;
031:
032: import org.apache.log4j.Logger;
033:
034: import de.schlund.pfixcore.beans.BeanDescriptorFactory;
035: import de.schlund.pfixcore.beans.InitException;
036: import de.schlund.pfixcore.beans.metadata.DefaultLocator;
037: import de.schlund.pfixcore.webservice.ProcessingInfo;
038: import de.schlund.pfixcore.webservice.ServiceCallContext;
039: import de.schlund.pfixcore.webservice.ServiceDescriptor;
040: import de.schlund.pfixcore.webservice.ServiceException;
041: import de.schlund.pfixcore.webservice.ServiceProcessor;
042: import de.schlund.pfixcore.webservice.ServiceRegistry;
043: import de.schlund.pfixcore.webservice.ServiceRequest;
044: import de.schlund.pfixcore.webservice.ServiceResponse;
045: import de.schlund.pfixcore.webservice.ServiceRuntime;
046: import de.schlund.pfixcore.webservice.config.ServiceConfig;
047: import de.schlund.pfixcore.webservice.fault.Fault;
048: import de.schlund.pfixcore.webservice.fault.FaultHandler;
049: import de.schlund.pfixcore.webservice.json.JSONArray;
050: import de.schlund.pfixcore.webservice.json.JSONObject;
051: import de.schlund.pfixcore.webservice.json.parser.JSONParser;
052:
053: /**
054: * @author mleidig@schlund.de
055: */
056: public class JSONWSProcessor implements ServiceProcessor {
057:
058: private Logger LOG = Logger.getLogger(JSONWSProcessor.class);
059:
060: private BeanDescriptorFactory beanDescFactory;
061: private SerializerRegistry serializerRegistry;
062: private DeserializerRegistry deserializerRegistry;
063:
064: public JSONWSProcessor(URL defaultBeanMetaDataURL)
065: throws ServiceException {
066: try {
067: beanDescFactory = new BeanDescriptorFactory(
068: new DefaultLocator(defaultBeanMetaDataURL));
069: } catch (InitException x) {
070: throw new ServiceException(
071: "BeanDescriptorFactory initialization failed.", x);
072: }
073: serializerRegistry = new SerializerRegistry(beanDescFactory);
074: deserializerRegistry = new DeserializerRegistry(beanDescFactory);
075: }
076:
077: public void process(ServiceRequest req, ServiceResponse res,
078: ServiceRuntime runtime, ServiceRegistry registry,
079: ProcessingInfo procInfo) throws ServiceException {
080: Writer writer = null;
081: try {
082:
083: String serviceName = req.getServiceName();
084: ServiceConfig service = registry.getService(serviceName);
085: if (service == null)
086: throw new ServiceException("Service not found: "
087: + serviceName);
088:
089: if (req.getParameter("json") != null) {
090:
091: //Get service description
092: JSONObject jsonRes = listMethods(service, runtime,
093: registry);
094: res.setContentType("text/plain");
095: res.setCharacterEncoding("utf-8");
096: res.setMessage(jsonRes.toJSONString());
097:
098: } else {
099:
100: Throwable error = null;
101: String jsonData = req.getMessage();
102: JSONObject jsonReq = null;
103: Object resultObject = null;
104:
105: //Parsing
106: try {
107: long t1 = System.currentTimeMillis();
108: JSONParser parser = new JSONParser(
109: new StringReader(jsonData));
110: jsonReq = (JSONObject) parser.getJSONValue();
111: long t2 = System.currentTimeMillis();
112: if (LOG.isDebugEnabled())
113: LOG.debug("Parsing: " + (t2 - t1) + "ms");
114: } catch (Throwable t) {
115: error = new ServiceException(
116: "Error during parsing", t);
117: }
118:
119: if (error == null) {
120:
121: //Service method lookup
122: Method method = null;
123: JSONArray params = jsonReq.getArrayMember("params");
124: JSONDeserializer jsonDeser = new JSONDeserializer(
125: deserializerRegistry);
126: try {
127: String methodName = jsonReq
128: .getStringMember("method");
129: ServiceDescriptor serviceDesc = runtime
130: .getServiceDescriptorCache()
131: .getServiceDescriptor(service);
132: List<Method> methods = serviceDesc
133: .getMethods(methodName);
134: if (methods.size() == 0)
135: throw new ServiceException(
136: "Method not found: " + methodName);
137: else if (methods.size() == 1)
138: method = methods.get(0);
139: else {
140: //ambiguous methods, guess the right one
141: Iterator<Method> methIt = methods
142: .iterator();
143: while (methIt.hasNext() && method == null) {
144: Method testMeth = methIt.next();
145: Type[] types = testMeth
146: .getGenericParameterTypes();
147: if (types.length == params.size()) {
148: boolean canDeserialize = true;
149: for (int i = 0; i < params.size()
150: && canDeserialize; i++) {
151: if (!jsonDeser
152: .canDeserialize(params
153: .get(i),
154: types[i]))
155: canDeserialize = false;
156: }
157: if (canDeserialize)
158: method = testMeth;
159: }
160: }
161: }
162: if (method == null)
163: throw new ServiceException(
164: "No matching method found: "
165: + methodName);
166: } catch (Throwable t) {
167: error = new ServiceException(
168: "Error during method lookup", t);
169: }
170:
171: if (error == null) {
172:
173: //Deserialization
174: Object[] paramObjects = null;
175: try {
176: long t1 = System.currentTimeMillis();
177: paramObjects = new Object[params.size()];
178: Type[] types = method
179: .getGenericParameterTypes();
180: for (int i = 0; i < params.size(); i++) {
181: Object obj = params.get(i);
182: Object deserObj = jsonDeser
183: .deserialize(obj, types[i]);
184: paramObjects[i] = deserObj;
185: }
186: long t2 = System.currentTimeMillis();
187: if (LOG.isDebugEnabled())
188: LOG.debug("Deserialization: "
189: + (t2 - t1) + "ms");
190: } catch (Throwable t) {
191: error = new ServiceException(
192: "Error during deserialization", t);
193: }
194:
195: if (error == null) {
196:
197: procInfo.setService(serviceName);
198: procInfo.setMethod(method.getName());
199: procInfo.startInvocation();
200:
201: //Invocation
202: try {
203: Object serviceObject = registry
204: .getServiceObject(serviceName);
205: resultObject = method.invoke(
206: serviceObject, paramObjects);
207: } catch (Throwable t) {
208: if (t instanceof InvocationTargetException
209: && t.getCause() != null)
210: error = t.getCause();
211: else
212: error = new ServiceException(
213: "Error during invocation",
214: t);
215: }
216:
217: procInfo.endInvocation();
218: if (LOG.isDebugEnabled())
219: LOG.debug("Invocation: "
220: + procInfo.getInvocationTime()
221: + "ms");
222: }
223: }
224: }
225:
226: res.setContentType("text/plain");
227: res.setCharacterEncoding("utf-8");
228: writer = res.getMessageWriter();
229: writer.write("{");
230: if (jsonReq != null && jsonReq.hasMember("id")) {
231: writer.write("\"id\":");
232: writer.write("\"" + jsonReq.getStringMember("id")
233: + "\"");
234: writer.write(",");
235: }
236:
237: if (error == null) {
238: writer.write("\"result\":");
239: //Serialization
240: long t1 = System.currentTimeMillis();
241: if (resultObject instanceof Void
242: || resultObject == null) {
243: writer.write("null");
244: } else {
245: JSONSerializer jsonSer = new JSONSerializer(
246: serializerRegistry, service
247: .getJSONClassHinting());
248: jsonSer.serialize(resultObject, writer);
249: }
250: long t2 = System.currentTimeMillis();
251: if (LOG.isDebugEnabled())
252: LOG.debug("Serialization: " + (t2 - t1) + "ms");
253: } else {
254: //Handle error
255: LOG.error(error, error);
256: try {
257: LOG.error(req.dump());
258: } catch (Exception x) {
259: LOG.error("No dump available", x);
260: }
261: ServiceCallContext callContext = ServiceCallContext
262: .getCurrentContext();
263: Fault fault = new Fault(serviceName, callContext
264: .getServiceRequest(), callContext
265: .getServiceResponse(), jsonData,
266: callContext.getContext());
267: fault.setThrowable(error);
268: FaultHandler faultHandler = service
269: .getFaultHandler();
270: if (faultHandler == null)
271: faultHandler = runtime.getConfiguration()
272: .getGlobalServiceConfig()
273: .getFaultHandler();
274: if (faultHandler != null)
275: faultHandler.handleFault(fault);
276: error = fault.getThrowable();
277: JSONObject errobj = new JSONObject();
278: errobj
279: .putMember("name", error.getClass()
280: .getName());
281: errobj.putMember("message", error.getMessage());
282: writer.write("\"error\":");
283: writer.write(errobj.toJSONString());
284: }
285: writer.write("}");
286: writer.flush();
287: }
288: } catch (Exception e) {
289: ServiceException se = new ServiceException(
290: "Error while processing service request.", e);
291: LOG.error(se);
292: try {
293: LOG.error(req.dump());
294: } catch (Exception x) {
295: LOG.error("No dump available", x);
296: }
297: throw se;
298: } finally {
299: if (writer != null) {
300: try {
301: writer.close();
302: } catch (IOException x) {
303: }
304: }
305: }
306: }
307:
308: public void processException(ServiceRequest req,
309: ServiceResponse res, Exception exception)
310: throws ServiceException {
311: try {
312: res.setContentType("text/plain");
313: res.setCharacterEncoding("utf-8");
314: Writer writer = res.getMessageWriter();
315: writer.write("{");
316: JSONObject errobj = new JSONObject();
317: errobj.putMember("name", exception.getClass().getName());
318: errobj.putMember("message", exception.getMessage());
319: writer.write("\"error\":");
320: writer.write(errobj.toJSONString());
321: writer.write("}");
322: writer.flush();
323: writer.close();
324: } catch (IOException x) {
325: throw new ServiceException(
326: "IOException during service exception processing.",
327: x);
328: }
329: }
330:
331: private JSONObject listMethods(ServiceConfig service,
332: ServiceRuntime runtime, ServiceRegistry srvReg)
333: throws ServiceException {
334: JSONArray meths = new JSONArray();
335: ServiceDescriptor desc = runtime.getServiceDescriptorCache()
336: .getServiceDescriptor(service);
337: if (desc != null) {
338: for (String methName : desc.getMethods())
339: meths.add(methName);
340: JSONObject resObj = new JSONObject();
341: resObj.putMember("result", meths);
342: resObj.putMember("id", 0);
343: return resObj;
344: } else
345: throw new ServiceException("Unknown service: "
346: + service.getName());
347: }
348:
349: }
|