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;
021:
022: import java.io.ByteArrayOutputStream;
023: import java.io.IOException;
024: import java.util.HashMap;
025: import java.util.Map;
026:
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029: import javax.servlet.http.HttpSession;
030:
031: import org.apache.log4j.Logger;
032:
033: import de.schlund.pfixcore.webservice.config.Configuration;
034: import de.schlund.pfixcore.webservice.config.GlobalServiceConfig;
035: import de.schlund.pfixcore.webservice.config.ServiceConfig;
036: import de.schlund.pfixcore.webservice.monitor.Monitor;
037: import de.schlund.pfixcore.webservice.monitor.MonitorRecord;
038: import de.schlund.pfixcore.webservice.utils.FileCache;
039: import de.schlund.pfixcore.webservice.utils.FileCacheData;
040: import de.schlund.pfixcore.webservice.utils.RecordingRequestWrapper;
041: import de.schlund.pfixcore.webservice.utils.RecordingResponseWrapper;
042: import de.schlund.pfixcore.workflow.ContextImpl;
043: import de.schlund.pfixcore.workflow.context.ServerContextImpl;
044: import de.schlund.pfixxml.ServerContextStore;
045: import de.schlund.pfixxml.SessionContextStore;
046: import de.schlund.pfixxml.serverutil.SessionAdmin;
047:
048: /**
049: * @author mleidig@schlund.de
050: */
051: public class ServiceRuntime {
052:
053: private final static Logger LOG = Logger
054: .getLogger(ServiceRuntime.class);
055: private final static Logger LOGGER_WSTRAIL = Logger
056: .getLogger("LOGGER_WSTRAIL");
057:
058: private static ThreadLocal<ServiceCallContext> currentContext = new ThreadLocal<ServiceCallContext>();
059:
060: private Configuration configuration;
061: private Monitor monitor;
062:
063: private Map<String, ServiceProcessor> processors;
064: private Map<String, ServiceStubGenerator> generators;
065: private String defaultProtocol;
066:
067: private FileCache stubCache;
068:
069: private ServiceDescriptorCache srvDescCache;
070: private ServiceRegistry appServiceRegistry;
071:
072: public ServiceRuntime() {
073: srvDescCache = new ServiceDescriptorCache();
074: processors = new HashMap<String, ServiceProcessor>();
075: generators = new HashMap<String, ServiceStubGenerator>();
076: stubCache = new FileCache(100);
077: }
078:
079: public ServiceDescriptorCache getServiceDescriptorCache() {
080: return srvDescCache;
081: }
082:
083: public void addServiceProcessor(String protocol,
084: ServiceProcessor processor) {
085: if (processors.isEmpty())
086: defaultProtocol = protocol;
087: processors.put(protocol, processor);
088: }
089:
090: public void addServiceStubGenerator(String protocol,
091: ServiceStubGenerator generator) {
092: generators.put(protocol, generator);
093: }
094:
095: public Configuration getConfiguration() {
096: return configuration;
097: }
098:
099: public void setConfiguration(Configuration configuration) {
100: this .configuration = configuration;
101: GlobalServiceConfig globConf = configuration
102: .getGlobalServiceConfig();
103: if (globConf.getMonitoringEnabled()) {
104: Monitor.Scope scope = Monitor.Scope.valueOf(globConf
105: .getMonitoringScope().toUpperCase());
106: monitor = new Monitor(scope, globConf
107: .getMonitoringHistorySize());
108: }
109: }
110:
111: public void setApplicationServiceRegistry(
112: ServiceRegistry appServiceRegistry) {
113: this .appServiceRegistry = appServiceRegistry;
114: }
115:
116: public Monitor getMonitor() {
117: return monitor;
118: }
119:
120: public void process(HttpServletRequest req, HttpServletResponse res)
121: throws ServiceException {
122:
123: ContextImpl pfxSessionContext = null;
124: String wsType = null;
125: ServiceRequest serviceReq = new HttpServiceRequest(req);
126: ServiceResponse serviceRes = new HttpServiceResponse(res);
127:
128: GlobalServiceConfig globConf = getConfiguration()
129: .getGlobalServiceConfig();
130: boolean doRecord = globConf.getMonitoringEnabled()
131: || globConf.getLoggingEnabled();
132: if (doRecord) {
133: serviceReq = new RecordingRequestWrapper(serviceReq);
134: serviceRes = new RecordingResponseWrapper(serviceRes);
135: }
136:
137: try {
138:
139: ServiceCallContext callContext = new ServiceCallContext(
140: this );
141: callContext.setServiceRequest(serviceReq);
142: callContext.setServiceResponse(serviceRes);
143: setCurrentContext(callContext);
144:
145: String serviceName = serviceReq.getServiceName();
146:
147: wsType = req.getHeader(Constants.HEADER_WSTYPE);
148: if (wsType == null)
149: wsType = req.getParameter(Constants.PARAM_WSTYPE);
150: if (wsType != null)
151: wsType = wsType.toUpperCase();
152:
153: HttpSession session = req.getSession(false);
154:
155: ServiceRegistry serviceReg = null;
156: ServiceConfig srvConf = appServiceRegistry
157: .getService(serviceName);
158: if (srvConf != null)
159: serviceReg = appServiceRegistry;
160: else {
161: if (session != null) {
162: serviceReg = (ServiceRegistry) session
163: .getAttribute(ServiceRegistry.class
164: .getName());
165: if (serviceReg == null) {
166: serviceReg = new ServiceRegistry(configuration,
167: ServiceRegistry.RegistryType.SESSION);
168: session.setAttribute(ServiceRegistry.class
169: .getName(), serviceReg);
170: }
171: srvConf = serviceReg.getService(serviceName);
172: }
173: }
174: if (srvConf == null)
175: throw new ServiceException("Service not found: "
176: + serviceName);
177:
178: if (srvConf.getContextName() != null) {
179: if (srvConf.getSessionType().equals(
180: Constants.SESSION_TYPE_SERVLET)) {
181: if (session == null)
182: throw new AuthenticationException(
183: "Authentication failed: No valid session.");
184: if (srvConf.getSSLForce()
185: && !req.getScheme().equals("https"))
186: throw new AuthenticationException(
187: "Authentication failed: SSL connection required");
188: if (req.getScheme().equals("https")) {
189: Boolean secure = (Boolean) session
190: .getAttribute(SessionAdmin.SESSION_IS_SECURE);
191: if (secure == null || !secure.booleanValue())
192: throw new AuthenticationException(
193: "Authentication failed: No secure session");
194: }
195:
196: pfxSessionContext = SessionContextStore
197: .getInstance(session).getContext(
198: srvConf.getContextName());
199: if (pfxSessionContext == null)
200: throw new ServiceException("Context '"
201: + srvConf.getContextName()
202: + "' doesn't exist.");
203:
204: ServerContextImpl srvContext = ServerContextStore
205: .getInstance(session.getServletContext())
206: .getContext(srvConf.getContextName());
207: if (srvContext == null)
208: throw new ServiceException("ServerContext '"
209: + srvConf.getContextName()
210: + "' doesn't exist.");
211:
212: try {
213: // Prepare context for current thread.
214: // Cleanup is performed in finally block.
215: pfxSessionContext.setServerContext(srvContext);
216: pfxSessionContext.prepareForRequest();
217: if (!pfxSessionContext.isAuthorized())
218: throw new AuthenticationException(
219: "Authorization failed: Must authenticate first!");
220: } catch (Exception x) {
221: if (x instanceof AuthenticationException)
222: throw (AuthenticationException) x;
223: else
224: throw new AuthenticationException(
225: "Authorization failed", x);
226: }
227: callContext.setContext(pfxSessionContext);
228: }
229: }
230:
231: String protocolType = srvConf.getProtocolType();
232: if (protocolType == null)
233: protocolType = getConfiguration()
234: .getGlobalServiceConfig().getProtocolType();
235:
236: if (wsType != null) {
237: if (!protocolType.equals(Constants.PROTOCOL_TYPE_ANY)
238: && !wsType.equals(protocolType))
239: throw new ServiceException("Service protocol '"
240: + wsType + "' isn't supported.");
241: else
242: protocolType = wsType;
243: }
244:
245: if (protocolType.equals(Constants.PROTOCOL_TYPE_ANY))
246: protocolType = defaultProtocol;
247: ServiceProcessor processor = processors.get(protocolType);
248: if (processor == null)
249: throw new ServiceException(
250: "No ServiceProcessor found for protocol '"
251: + protocolType + "'.");
252:
253: if (LOG.isDebugEnabled())
254: LOG.debug("Process webservice request: " + serviceName
255: + " " + processor);
256:
257: ProcessingInfo procInfo = new ProcessingInfo(serviceName,
258: null);
259: procInfo.setStartTime(System.currentTimeMillis());
260: procInfo.startProcessing();
261:
262: if (pfxSessionContext != null
263: && srvConf.getSynchronizeOnContext()) {
264: synchronized (pfxSessionContext) {
265: processor.process(serviceReq, serviceRes, this ,
266: serviceReg, procInfo);
267: }
268: } else {
269: processor.process(serviceReq, serviceRes, this ,
270: serviceReg, procInfo);
271: }
272:
273: procInfo.endProcessing();
274:
275: if (session != null) {
276: StringBuilder line = new StringBuilder();
277: line.append(session.getId() + "|");
278: line.append(req.getRemoteAddr() + "|");
279: line.append(req.getServerName() + "|");
280: line.append(req.getRequestURI() + "|");
281: line.append(serviceName + "|");
282: line.append((procInfo.getMethod() == null ? "-"
283: : procInfo.getMethod())
284: + "|");
285: line.append(procInfo.getProcessingTime() + "|");
286: line.append((procInfo.getInvocationTime() == -1 ? "-"
287: : procInfo.getInvocationTime()));
288: LOGGER_WSTRAIL.warn(line.toString());
289: }
290:
291: if (doRecord) {
292: RecordingRequestWrapper monitorReq = (RecordingRequestWrapper) serviceReq;
293: RecordingResponseWrapper monitorRes = (RecordingResponseWrapper) serviceRes;
294: String reqMsg = monitorReq.getRecordedMessage();
295: String resMsg = monitorRes.getRecordedMessage();
296: if (globConf.getMonitoringEnabled()) {
297: MonitorRecord monitorRecord = new MonitorRecord();
298: monitorRecord.setStartTime(procInfo.getStartTime());
299: monitorRecord.setProcessingTime(procInfo
300: .getProcessingTime());
301: monitorRecord.setInvocationTime(procInfo
302: .getInvocationTime());
303: monitorRecord.setProtocol(protocolType);
304: monitorRecord.setService(serviceName);
305: monitorRecord.setMethod(procInfo.getMethod());
306: monitorRecord.setRequestMessage(reqMsg);
307: monitorRecord.setResponseMessage(resMsg);
308: getMonitor().getMonitorHistory(req).addRecord(
309: monitorRecord);
310: }
311: if (globConf.getLoggingEnabled()) {
312: StringBuffer sb = new StringBuffer();
313: sb.append("\nService: " + serviceName + "\n");
314: sb.append("Protocol: " + protocolType + "\n");
315: sb.append("Time: " + 0 + "\n");
316: sb.append("Request:\n");
317: sb.append(reqMsg == null ? "" : reqMsg);
318: sb.append("\nResponse:\n");
319: sb.append(resMsg);
320: sb.append("\n");
321: LOG.info(sb.toString());
322: }
323: }
324: } catch (AuthenticationException x) {
325: if (LOG.isDebugEnabled())
326: LOG.debug(x);
327: ServiceProcessor processor = processors.get(wsType);
328: if (processor != null)
329: processor.processException(serviceReq, serviceRes, x);
330: else
331: throw x;
332: } finally {
333: setCurrentContext(null);
334: if (pfxSessionContext != null) {
335: pfxSessionContext.cleanupAfterRequest();
336: }
337: }
338: }
339:
340: private static void setCurrentContext(ServiceCallContext callContext) {
341: currentContext.set(callContext);
342: }
343:
344: protected static ServiceCallContext getCurrentContext() {
345: return currentContext.get();
346: }
347:
348: public void getStub(HttpServletRequest req, HttpServletResponse res)
349: throws ServiceException, IOException {
350:
351: long t1 = System.currentTimeMillis();
352:
353: String nameParam = req.getParameter("name");
354: if (nameParam == null)
355: throw new ServiceException("Missing parameter: name");
356: String typeParam = req.getParameter("type");
357: if (typeParam == null)
358: throw new ServiceException("Missing parameter: type");
359: nameParam = nameParam.trim();
360: String[] serviceNames = nameParam.split("\\s+");
361: if (serviceNames.length == 0)
362: throw new ServiceException("No service name found");
363: String serviceType = typeParam.toUpperCase();
364: if (!serviceType.equals(Constants.PROTOCOL_TYPE_JSONWS))
365: throw new ServiceException("Protocol not supported: "
366: + serviceType);
367:
368: ServiceConfig[] services = new ServiceConfig[serviceNames.length];
369: for (int i = 0; i < serviceNames.length; i++) {
370: HttpSession session = req.getSession(false);
371: ServiceConfig service = appServiceRegistry
372: .getService(serviceNames[i]);
373: if (service == null) {
374: if (session != null) {
375: ServiceRegistry serviceReg = (ServiceRegistry) session
376: .getAttribute(ServiceRegistry.class
377: .getName());
378: if (serviceReg == null) {
379: serviceReg = new ServiceRegistry(configuration,
380: ServiceRegistry.RegistryType.SESSION);
381: session.setAttribute(ServiceRegistry.class
382: .getName(), serviceReg);
383: }
384: service = serviceReg.getService(serviceNames[i]);
385: }
386: }
387: if (service == null)
388: throw new ServiceException("Service not found: "
389: + serviceNames[i]);
390: services[i] = service;
391: }
392:
393: StringBuilder sb = new StringBuilder();
394: for (String serviceName : serviceNames) {
395: sb.append(serviceName);
396: sb.append(" ");
397: }
398: String cacheKey = sb.toString();
399:
400: FileCacheData data = stubCache.get(cacheKey);
401: if (data == null) {
402: ServiceStubGenerator stubGen = generators.get(serviceType);
403: if (stubGen != null) {
404: long tt1 = System.currentTimeMillis();
405: ByteArrayOutputStream bout = new ByteArrayOutputStream();
406: for (int i = 0; i < services.length; i++) {
407: stubGen.generateStub(services[i], bout);
408: if (i < services.length - 1)
409: bout.write("\n\n".getBytes());
410: }
411: byte[] bytes = bout.toByteArray();
412: data = new FileCacheData(bytes);
413: stubCache.put(cacheKey, data);
414: long tt2 = System.currentTimeMillis();
415: if (LOG.isDebugEnabled())
416: LOG.debug("Generated stub for '" + cacheKey
417: + "' (Time: " + (tt2 - tt1) + "ms, Size: "
418: + bytes.length + "b)");
419: } else
420: throw new ServiceException(
421: "No stub generator found for protocol: "
422: + serviceType);
423: }
424:
425: long t2 = System.currentTimeMillis();
426: if (LOG.isDebugEnabled())
427: LOG.debug("Retrieved stub for '" + cacheKey + "' (Time: "
428: + (t2 - t1) + "ms)");
429:
430: String etag = req.getHeader("If-None-Match");
431: if (etag != null && etag.equals(data.getMD5())) {
432: res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
433: } else {
434: res.setContentType("text/plain");
435: res.setContentLength(data.getBytes().length);
436: res.setHeader("ETag", data.getMD5());
437: res.getOutputStream().write(data.getBytes());
438: res.getOutputStream().close();
439: }
440: }
441:
442: }
|