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.pfixxml;
021:
022: import java.util.Properties;
023: import java.util.TreeMap;
024:
025: import javax.servlet.ServletException;
026: import javax.servlet.http.HttpSession;
027:
028: import org.apache.log4j.Logger;
029:
030: import de.schlund.pfixcore.exception.PustefixApplicationException;
031: import de.schlund.pfixcore.exception.PustefixCoreException;
032: import de.schlund.pfixcore.exception.PustefixRuntimeException;
033: import de.schlund.pfixcore.scriptedflow.ScriptedFlowConfig;
034: import de.schlund.pfixcore.scriptedflow.ScriptedFlowInfo;
035: import de.schlund.pfixcore.scriptedflow.compiler.CompilerException;
036: import de.schlund.pfixcore.scriptedflow.vm.Script;
037: import de.schlund.pfixcore.scriptedflow.vm.ScriptVM;
038: import de.schlund.pfixcore.scriptedflow.vm.VirtualHttpServletRequest;
039: import de.schlund.pfixcore.workflow.ContextImpl;
040: import de.schlund.pfixcore.workflow.ExtendedContext;
041: import de.schlund.pfixcore.workflow.context.RequestContextImpl;
042: import de.schlund.pfixcore.workflow.context.ServerContextImpl;
043: import de.schlund.pfixxml.config.AbstractXMLServletConfig;
044: import de.schlund.pfixxml.config.ConfigReader;
045: import de.schlund.pfixxml.config.ContextXMLServletConfig;
046: import de.schlund.pfixxml.config.PageRequestConfig;
047: import de.schlund.pfixxml.resources.FileResource;
048:
049: /**
050: * @author jtl
051: *
052: */
053:
054: public class ContextXMLServlet extends AbstractXMLServlet {
055: private Logger LOG = Logger.getLogger(ContextXMLServlet.class);
056:
057: // private final static String ALREADY_SSL = "__CONTEXT_ALREADY_SSL__";
058:
059: private final static String PARAM_SCRIPTEDFLOW = "__scriptedflow";
060:
061: private final static String SCRIPTEDFLOW_SUFFIX = "__SCRIPTEDFLOW__";
062:
063: public final static String XSLPARAM_REQUESTCONTEXT = "__context__";
064:
065: private ContextXMLServletConfig config = null;
066:
067: private ServerContextImpl servercontext = null;
068:
069: private Object reloadInitLock = new Object();
070: private boolean reloadInitDone;
071:
072: protected ContextXMLServletConfig getContextXMLServletConfig() {
073: return this .config;
074: }
075:
076: protected AbstractXMLServletConfig getAbstractXMLServletConfig() {
077: return this .config;
078: }
079:
080: protected boolean needsSSL(PfixServletRequest preq)
081: throws ServletException {
082: if (super .needsSSL(preq)) {
083: return true;
084: } else {
085: String pagename = preq.getPageName();
086: if (pagename != null) {
087: PageRequestConfig pageConfig = config
088: .getContextConfig().getPageRequestConfig(
089: pagename);
090: if (pageConfig != null) {
091: return pageConfig.isSSL();
092: }
093: }
094: }
095: return false;
096: }
097:
098: protected boolean needsSession() {
099: return true;
100: }
101:
102: protected boolean allowSessionCreate() {
103: return true;
104: }
105:
106: protected boolean tryReloadProperties(PfixServletRequest preq)
107: throws ServletException {
108: //synchronize first method call because of a race condition, which
109: //can lead to a NullPointerException (servercontext being null)
110: synchronized (reloadInitLock) {
111: if (!reloadInitDone) {
112: boolean result = nosyncTryReloadProperties(preq);
113: reloadInitDone = true;
114: return result;
115: }
116: }
117: return nosyncTryReloadProperties(preq);
118: }
119:
120: private boolean nosyncTryReloadProperties(PfixServletRequest preq)
121: throws ServletException {
122: if (super .tryReloadProperties(preq)) {
123: try {
124: servercontext = new ServerContextImpl(
125: getContextXMLServletConfig().getContextConfig(),
126: servletname);
127: ServerContextStore
128: .getInstance(this .getServletContext())
129: .storeContext(this , preq, servletname,
130: servercontext);
131: } catch (Exception e) {
132: String msg = "Error during reload of servlet configuration";
133: LOG.error(msg, e);
134: throw new ServletException(msg, e);
135: }
136: return true;
137: } else {
138: return false;
139: }
140: }
141:
142: public SPDocument getDom(PfixServletRequest preq)
143: throws PustefixApplicationException, PustefixCoreException {
144: ExtendedContext context = getContext(preq);
145:
146: // Prepare context for current thread
147: // Cleanup is performed in finally block
148: ((ContextImpl) context).prepareForRequest();
149:
150: try {
151: SPDocument spdoc;
152:
153: ScriptedFlowInfo info = getScriptedFlowInfo(preq);
154: if (preq.getRequestParam(PARAM_SCRIPTEDFLOW) != null
155: && preq.getRequestParam(PARAM_SCRIPTEDFLOW)
156: .getValue() != null) {
157: String scriptedFlowName = preq.getRequestParam(
158: PARAM_SCRIPTEDFLOW).getValue();
159:
160: // Do a virtual request without any request parameters
161: // to get an initial SPDocument
162: PfixServletRequest vpreq = new PfixServletRequestImpl(
163: VirtualHttpServletRequest.getVoidRequest(preq
164: .getRequest()),
165: getContextXMLServletConfig().getProperties());
166: spdoc = context.handleRequest(vpreq);
167:
168: // Reset current scripted flow state
169: info.reset();
170:
171: // Lookup script name
172: Script script;
173: try {
174: script = getScriptedFlowByName(scriptedFlowName);
175: } catch (CompilerException e) {
176: throw new PustefixCoreException(
177: "Could not compile scripted flow "
178: + scriptedFlowName, e);
179: }
180:
181: if (script != null) {
182: // Remember running script
183: info.isScriptRunning(true);
184:
185: // Get parameters for scripted flow:
186: // They have the form __scriptedflow.<name>=<value>
187: String[] paramNames = preq.getRequestParamNames();
188: for (int i = 0; i < paramNames.length; i++) {
189: if (!paramNames[i].equals(PARAM_SCRIPTEDFLOW)) {
190: String paramName = paramNames[i];
191: String paramValue = preq.getRequestParam(
192: paramName).getValue();
193: info.addParam(paramName, paramValue);
194: }
195: }
196:
197: // Create VM and run script
198: ScriptVM vm = new ScriptVM();
199: vm.setScript(script);
200: try {
201: spdoc = vm.run(preq, spdoc, context, info
202: .getParams());
203: } finally {
204: // Make sure this is done even if an error has occured
205: if (vm.isExitState()) {
206: info.reset();
207: } else {
208: info.setState(vm.saveVMState());
209: }
210: }
211: }
212:
213: } else if (info.isScriptRunning()) {
214: // First handle user request, then use result document
215: // as base for further processing
216: spdoc = context.handleRequest(preq);
217:
218: // Create VM and run script
219: ScriptVM vm = new ScriptVM();
220: vm.loadVMState(info.getState());
221: try {
222: spdoc = vm.run(preq, spdoc, context, info
223: .getParams());
224: } finally {
225: if (vm.isExitState()) {
226: info.reset();
227: } else {
228: info.setState(vm.saveVMState());
229: }
230: }
231: } else {
232: // No scripted flow request
233: // handle as usual
234: spdoc = context.handleRequest(preq);
235: }
236:
237: if (spdoc != null
238: && !spdoc.isRedirect()
239: && (preq.getPageName() == null || !preq
240: .getPageName().equals(spdoc.getPagename()))) {
241: // Make sure all requests that don't encode an explicite pagename
242: // (this normally is only the case for the first request)
243: // OR pages that have the "wrong" pagename in their request
244: // (this applies to pages selected by stepping ahead in the page flow)
245: // are redirected to the page selected by the business logic below
246: String scheme = preq.getScheme();
247: String port = String.valueOf(preq.getServerPort());
248: String redirectURL = scheme
249: + "://"
250: + ServletManager.getServerName(preq
251: .getRequest()) + ":" + port
252: + preq.getContextPath() + preq.getServletPath()
253: + "/" + spdoc.getPagename() + ";jsessionid="
254: + preq.getSession(false).getId() + "?__reuse="
255: + spdoc.getTimestamp();
256: RequestParam rp = preq.getRequestParam("__frame");
257: if (rp != null) {
258: redirectURL += "&__frame=" + rp.getValue();
259: }
260: spdoc.setRedirect(redirectURL);
261:
262: }
263:
264: return spdoc;
265: } finally {
266: ((ContextImpl) context).cleanupAfterRequest();
267: }
268: }
269:
270: private Script getScriptedFlowByName(String scriptedFlowName)
271: throws CompilerException {
272: ScriptedFlowConfig config = getContextXMLServletConfig()
273: .getScriptedFlowConfig();
274: return config.getScript(scriptedFlowName);
275: }
276:
277: private ScriptedFlowInfo getScriptedFlowInfo(PfixServletRequest preq) {
278: // Context is already loaded at this time, so we cann assume
279: // that there is a valid session
280: String name = servletname + SCRIPTEDFLOW_SUFFIX;
281: ScriptedFlowInfo info = (ScriptedFlowInfo) preq.getSession(
282: false).getAttribute(name);
283: if (info == null) {
284: info = new ScriptedFlowInfo();
285: preq.getSession(false).setAttribute(name, info);
286: }
287: return info;
288: }
289:
290: private ExtendedContext getContext(PfixServletRequest preq)
291: throws PustefixApplicationException, PustefixCoreException {
292: HttpSession session = preq.getSession(false);
293: if (session == null) {
294: // The ServletManager class handles session creation
295: throw new PustefixRuntimeException(
296: "No valid session found! Aborting...");
297: }
298:
299: SessionContextStore store = SessionContextStore
300: .getInstance(session);
301: ContextImpl context = store.getContext(this , preq);
302: // Session does not have a context yet?
303: if (context == null) {
304: // Synchronize on session object to make sure only ONE
305: // context per session is created
306: synchronized (session) {
307: context = store.getContext(this , preq);
308: if (context == null) {
309: context = new ContextImpl(servercontext, session);
310: store.storeContext(this , preq, this .servletname,
311: context);
312: }
313: }
314: }
315: // Update reference to server context as it might have changed
316: context.setServerContext(servercontext);
317:
318: return context;
319: }
320:
321: protected void reloadServletConfig(FileResource configFile,
322: Properties globalProperties) throws ServletException {
323: try {
324: this .config = ConfigReader.readContextXMLServletConfig(
325: configFile, globalProperties);
326: } catch (PustefixCoreException e) {
327: throw new ServletException(
328: "Could not read servlet configuration from "
329: + configFile.toURI(), e);
330: }
331: }
332:
333: protected void hookBeforeRender(PfixServletRequest preq,
334: SPDocument spdoc, TreeMap<String, Object> paramhash,
335: String stylesheet) {
336: super .hookBeforeRender(preq, spdoc, paramhash, stylesheet);
337: RequestContextImpl oldRequestContext = (RequestContextImpl) spdoc
338: .getProperties().get(XSLPARAM_REQUESTCONTEXT);
339: RequestContextImpl newRequestContext;
340: try {
341: newRequestContext = (RequestContextImpl) oldRequestContext
342: .clone();
343: } catch (CloneNotSupportedException e) {
344: throw new RuntimeException("Unexpected CloneException", e);
345: }
346: newRequestContext.setPfixServletRequest(preq);
347: newRequestContext.getParentContext()
348: .setRequestContextForCurrentThread(newRequestContext);
349: }
350:
351: protected void hookAfterRender(PfixServletRequest preq,
352: SPDocument spdoc, TreeMap<String, Object> paramhash,
353: String stylesheet) {
354: super .hookAfterRender(preq, spdoc, paramhash, stylesheet);
355: RequestContextImpl rcontext = (RequestContextImpl) spdoc
356: .getProperties().get(XSLPARAM_REQUESTCONTEXT);
357: rcontext.getParentContext().setRequestContextForCurrentThread(
358: null);
359: }
360:
361: }
|