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:
024: import javax.servlet.ServletConfig;
025: import javax.servlet.ServletException;
026: import javax.servlet.http.HttpServletResponse;
027: import javax.servlet.http.HttpSession;
028:
029: import org.apache.log4j.Logger;
030:
031: import de.schlund.pfixcore.exception.PustefixCoreException;
032: import de.schlund.pfixcore.workflow.ContextImpl;
033: import de.schlund.pfixcore.workflow.ContextResourceManager;
034: import de.schlund.pfixcore.workflow.DirectOutputPageMap;
035: import de.schlund.pfixcore.workflow.DirectOutputState;
036: import de.schlund.pfixcore.workflow.PageRequest;
037: import de.schlund.pfixcore.workflow.context.ServerContextImpl;
038: import de.schlund.pfixxml.config.ConfigReader;
039: import de.schlund.pfixxml.config.DirectOutputServletConfig;
040: import de.schlund.pfixxml.config.ServletManagerConfig;
041: import de.schlund.pfixxml.resources.FileResource;
042:
043: /**
044: * The <code>DirectOutputServlet</code> is a servlet that hijacks the {@link de.schlund.pfixcore.workflow.Context} of a
045: * comapnion ContextXMLServlet that runs in the same servlet session.
046: * It has no Context of it's own, but rather makes all the work itself, as
047: * there is no PageFlow handling involved.
048: * Instead of {@link de.schlund.pfixcore.workflow.State}s (as the Context of a ContextXMLServlet
049: * does) this kind of Servlet uses so called {@link
050: * DirectOutputStates}. These can write their output directly to the HttpServletResponse
051: * object. So there is also no XML/XSLT transformation involved.
052: * You can use this construct whenever you need to make things like
053: * images, pdfs available for download and you can not simply write out a static file.
054: * Consider e.g. a autogenerated pdf that needs information that is saved in some ContextResource.
055: * A DirectOutputState can do this, as it has the possibility to work with the Context of a
056: * foreign ContextXMLServlet servlet, getting all the information it needs from there, generating the
057: * pdf and streaming it directly to the OutputStream of the HttpServletResponse.
058: *
059: * If the foreign Context is of type AuthContext, the servlet
060: * additionally checks, if the Context is authenticated before trying to call any DirectOutputState.
061: *
062: * The servlet gets the foreign context by using the mandatory property
063: * <code>foreigncontextservlet.foreignservletname</code>.
064: * The value must be the servlet name of the ContextXMLServlet whose Context you want to use.
065: *
066: * @author <a href="mailto:jtl@schlund.de">Jens Lautenbacher</a>
067: * @version $Id: DirectOutputServlet.java 3092 2007-03-09 15:05:22Z smarsching $
068: */
069: public class DirectOutputServlet extends ServletManager {
070: private Logger LOG = Logger.getLogger(this .getClass());
071: private String ext_cname = null;
072: private DirectOutputPageMap pagemap = null;
073: private DirectOutputServletConfig config;
074:
075: private final static String PROP_CONTEXT_NAME = "servlet.contextname";
076:
077: /**
078: * The usual <code>needsSession</code> method. Is set to return
079: * true, as any other value wouldn't make sense (You need to get a
080: * Context from a running session after all)
081: *
082: * @return a <code>boolean</code> value
083: */
084: protected final boolean needsSession() {
085: return true;
086: }
087:
088: /**
089: * <code>allowSessionCreate</code> returns false. If the session is being created
090: * here, it will not have a saved Context anyway, so this makes no sense.
091: *
092: * @return a <code>boolean</code> value
093: */
094: protected final boolean allowSessionCreate() {
095: return false;
096: }
097:
098: /**
099: * <code>process</code> first tries to get the requested
100: * Context. if the Context is of type AuthContext, it checks the
101: * Authorization of the context first. After that, it asks the
102: * {@link de.schlund.pfixcore.workflow.DirectOutputPageMap} for a
103: * {@link de.schlund.pfixcore.workflow.DirectOutputState}
104: * that matches the current PageRequest (NOTE: this is NOT the
105: * pagerequest that is returned from the foreign Context as the
106: * current PageRequest!). The accessibility of the
107: * DirectOutputState is checked, then the handleRequest(Context,
108: * Properties, PfixServletRequest, HttpServletResponse) method of
109: * the DirectOutputState is called. NOTE: The properties parameter
110: * are the properties matching the current PageRequest. Again this
111: * is not what the foreign context would return!
112: *
113: * @param preq a <code>PfixServletRequest</code> value
114: * @param res a <code>HttpServletResponse</code> value
115: * @exception Exception if an error occurs
116: */
117: protected void process(PfixServletRequest preq,
118: HttpServletResponse res) throws Exception {
119: HttpSession session = preq.getSession(false);
120: if (session == null) {
121: //throw new RuntimeException("*** didn't get Session from request. ***");
122: LOG
123: .error("*** didn't get Session from request. Stop processing. ***");
124: res.sendError(HttpServletResponse.SC_FORBIDDEN,
125: "No session supplied");
126: return;
127: }
128:
129: ContextImpl context = SessionContextStore.getInstance(session)
130: .getContext(ext_cname);
131: if (context == null) {
132: throw new RuntimeException("*** didn't find Context "
133: + ext_cname + " in Session " + session.getId()
134: + ", maybe it's not yet initialized??? ***");
135: }
136: ServerContextImpl servercontext = ServerContextStore
137: .getInstance(getServletContext()).getContext(ext_cname);
138: if (servercontext == null) {
139: throw new RuntimeException(
140: "*** didn't find ServerContext "
141: + ext_cname
142: + " in ServletContext, maybe it's not yet initialized??? ***");
143: }
144:
145: // Make sure the context is initialized and deinitialized this thread
146: context.setServerContext(servercontext);
147: context.prepareForRequest();
148: try {
149: if (config.isSynchronized()) {
150: synchronized (context) {
151: doProcess(preq, res, servercontext, context);
152: }
153: } else {
154: doProcess(preq, res, servercontext, context);
155: }
156: } finally {
157: context.cleanupAfterRequest();
158: }
159: }
160:
161: protected void doProcess(PfixServletRequest preq,
162: HttpServletResponse res, ServerContextImpl servercontext,
163: ContextImpl context) throws Exception {
164: ContextResourceManager crm = context
165: .getContextResourceManager();
166:
167: // check the authentification first....
168: if (!context.isAuthorized()) {
169: LOG.info("Got request without authorization");
170: res.sendError(HttpServletResponse.SC_FORBIDDEN,
171: "Must authenticate first");
172: return;
173: }
174:
175: String pagename = preq.getPageName();
176: if (pagename == null || pagename.length() == 0) {
177: LOG.error("*** got request without page name ***");
178: res.sendError(HttpServletResponse.SC_NOT_FOUND,
179: "Must specify page name");
180: return;
181: }
182: PageRequest page = new PageRequest(pagename);
183: DirectOutputState state = pagemap.getDirectOutputState(page);
184: if (state != null) {
185: Properties props = config.getPageRequest(page.getName())
186: .getProperties();
187: boolean allowed = state.isAccessible(crm, props, preq);
188: if (allowed) {
189: try {
190: state.handleRequest(crm, props, preq, res);
191: } catch (Exception exep) {
192: if (!exep
193: .getClass()
194: .getName()
195: .equals(
196: "org.apache.catalina.connector.ClientAbortException")) {
197: throw exep;
198: }
199: }
200: } else {
201: throw new RuntimeException(
202: "*** Called DirectOutputState "
203: + state.getClass().getName()
204: + " for page " + page.getName()
205: + " without being accessible ***");
206: }
207: } else {
208: LOG.error("*** No DirectOutputState for page "
209: + page.getName() + " ***");
210: res.sendError(HttpServletResponse.SC_NOT_FOUND, "Page "
211: + page.getName() + " not found");
212: return;
213: }
214: }
215:
216: protected boolean tryReloadProperties(PfixServletRequest preq)
217: throws ServletException {
218: if (super .tryReloadProperties(preq)) {
219: initValues();
220: return true;
221: } else {
222: return false;
223: }
224: }
225:
226: private void initValues() throws ServletException {
227: String cname = this .config.getExternalServletName();
228: String initCName = this .getInitParameter(PROP_CONTEXT_NAME);
229: if (cname != null && !cname.equals("")) {
230: ext_cname = cname;
231: } else if (initCName != null && initCName.length() > 0) {
232: ext_cname = initCName;
233: } else {
234: throw new ServletException(
235: "*** Need external servlet name! *****");
236: }
237:
238: try {
239: pagemap = (DirectOutputPageMap) PropertyObjectManager
240: .getInstance()
241: .getConfigurableObject(
242: this .config,
243: de.schlund.pfixcore.workflow.DirectOutputPageMap.class);
244: } catch (Exception e) {
245: LOG.warn("==================> XPTN " + e.getMessage());
246: throw new ServletException(e.getMessage(), e);
247: }
248: }
249:
250: public void init(ServletConfig config) throws ServletException {
251: super .init(config);
252: initValues();
253: }
254:
255: protected ServletManagerConfig getServletManagerConfig() {
256: return this .config;
257: }
258:
259: protected void reloadServletConfig(FileResource configFile,
260: Properties globalProperties) throws ServletException {
261: try {
262: this .config = ConfigReader.readDirectOutputServletConfig(
263: configFile, globalProperties);
264: } catch (PustefixCoreException e) {
265: throw new ServletException("Error on reading config file "
266: + configFile.toURI(), e);
267: }
268:
269: }
270:
271: }
|