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: package de.schlund.pfixcore.workflow;
020:
021: import java.util.Collections;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.LinkedHashMap;
025: import java.util.LinkedHashSet;
026: import java.util.Map;
027: import java.util.Properties;
028: import java.util.Set;
029:
030: import javax.servlet.http.Cookie;
031: import javax.servlet.http.HttpSession;
032: import javax.servlet.http.HttpSessionBindingEvent;
033: import javax.servlet.http.HttpSessionBindingListener;
034:
035: import de.schlund.pfixcore.auth.Authentication;
036: import de.schlund.pfixcore.auth.AuthenticationImpl;
037: import de.schlund.pfixcore.auth.Role;
038: import de.schlund.pfixcore.exception.PustefixApplicationException;
039: import de.schlund.pfixcore.exception.PustefixCoreException;
040: import de.schlund.pfixcore.util.TokenManager;
041: import de.schlund.pfixcore.util.TokenUtils;
042: import de.schlund.pfixcore.workflow.context.AccessibilityChecker;
043: import de.schlund.pfixcore.workflow.context.PageFlow;
044: import de.schlund.pfixcore.workflow.context.RequestContextImpl;
045: import de.schlund.pfixcore.workflow.context.ServerContextImpl;
046: import de.schlund.pfixxml.AbstractXMLServlet;
047: import de.schlund.pfixxml.PfixServletRequest;
048: import de.schlund.pfixxml.SPDocument;
049: import de.schlund.pfixxml.ServletManager;
050: import de.schlund.pfixxml.Variant;
051: import de.schlund.pfixxml.config.ContextConfig;
052: import de.schlund.pfixxml.config.PageRequestConfig;
053: import de.schlund.util.statuscodes.StatusCode;
054:
055: public class ContextImpl implements Context, AccessibilityChecker,
056: ExtendedContext, TokenManager, HttpSessionBindingListener {
057:
058: /**
059: * Implementation of the session part of the context used by
060: * ContextXMLServlet, DirectOutputServlet and WebServiceServlet. This class
061: * should never be directly used by application developers.
062: *
063: * @author Sebastian Marsching <sebastian.marsching@1und1.de>
064: */
065: private class SessionContextImpl {
066: private HttpSession session;
067: private Variant variant = null;
068: private String visitId = null;
069: private ContextResourceManagerImpl crm;
070: private SessionEndNotificator sessionEndNotificator;
071: private Authentication authentication;
072:
073: // private Map<NavigationElement, Integer> navigationMap = new
074: // HashMap<NavigationElement, Integer>();
075: private Set<String> visitedPages = Collections
076: .synchronizedSet(new HashSet<String>());
077:
078: private Map<String, String> tokens;
079:
080: private class SessionEndNotificator implements
081: HttpSessionBindingListener {
082: private LinkedHashSet<SessionStatusListener> sessionListeners = new LinkedHashSet<SessionStatusListener>();
083:
084: public void valueBound(HttpSessionBindingEvent ev) {
085: // Ignore this event
086: }
087:
088: public void valueUnbound(HttpSessionBindingEvent ev) {
089: // Send event to registered listeners
090: synchronized (this ) {
091: for (SessionStatusListener l : sessionListeners) {
092: l
093: .sessionStatusChanged(new SessionStatusEvent(
094: SessionStatusEvent.Type.SESSION_DESTROYED));
095: }
096: }
097: }
098: }
099:
100: public SessionContextImpl(HttpSession session) {
101: this .session = session;
102: this .crm = new ContextResourceManagerImpl();
103: synchronized (this .getClass()) {
104: this .sessionEndNotificator = (SessionEndNotificator) this .session
105: .getAttribute("de.schlund.pfixcore.workflow.ContextImpl.SessionContextImpl.dummylistenerobject");
106: if (this .sessionEndNotificator == null) {
107: this .sessionEndNotificator = new SessionEndNotificator();
108: this .session
109: .setAttribute(
110: "de.schlund.pfixcore.workflow.ContextImpl.SessionContextImpl.dummylistenerobject",
111: this .sessionEndNotificator);
112: }
113: }
114: }
115:
116: private void init(Context context)
117: throws PustefixApplicationException,
118: PustefixCoreException {
119: if (getContextConfig().hasRoles()) {
120: this .authentication = new AuthenticationImpl(
121: servercontext);
122: for (Role role : getContextConfig().getInitialRoles())
123: this .authentication.addRole(role.getName());
124: }
125: crm.init(context, context.getContextConfig());
126: }
127:
128: public ContextResourceManager getContextResourceManager() {
129: return crm;
130: }
131:
132: public void setLanguage(String langcode) {
133: session
134: .setAttribute(AbstractXMLServlet.SESS_LANG,
135: langcode);
136: }
137:
138: public String getLanguage() {
139: try {
140: return (String) session
141: .getAttribute(AbstractXMLServlet.SESS_LANG);
142: } catch (IllegalStateException e) {
143: // May be thrown if session has been invalidated
144: return null;
145: }
146: }
147:
148: public Variant getVariant() {
149: return variant;
150: }
151:
152: public void setVariant(Variant variant) {
153: this .variant = variant;
154: }
155:
156: public String getVisitId() {
157: if (visitId == null) {
158: visitId = (String) session
159: .getAttribute(ServletManager.VISIT_ID);
160: if (visitId == null) {
161: throw new RuntimeException(
162: "visit_id not set, but asked for!!!!");
163: }
164: }
165: return visitId;
166: }
167:
168: public void addVisitedPage(String pagename) {
169: visitedPages.add(pagename);
170: }
171:
172: public boolean isVisitedPage(String pagename) {
173: return visitedPages.contains(pagename);
174: }
175:
176: public void invalidateToken(String tokenName) {
177: synchronized (this ) {
178: if (tokens != null)
179: tokens.remove(tokenName);
180: }
181: }
182:
183: public boolean isValidToken(String tokenName, String token) {
184: synchronized (this ) {
185: if (tokens == null)
186: return false;
187: String storedToken = tokens.get(tokenName);
188: return storedToken != null && storedToken.equals(token);
189: }
190: }
191:
192: public String getToken(String tokenName) {
193: synchronized (this ) {
194: if (tokens == null)
195: tokens = new LinkedHashMap<String, String>();
196: String token = TokenUtils.createRandomToken();
197: if (tokens.size() > 25) {
198: Iterator<String> it = tokens.keySet().iterator();
199: it.next();
200: it.remove();
201: }
202: tokens.put(tokenName, token);
203: return token;
204: }
205: }
206:
207: public Authentication getAuthentication() {
208: return authentication;
209: }
210:
211: public void setAuthentication(Authentication authentication) {
212: this .authentication = authentication;
213: }
214:
215: @Override
216: public String toString() {
217: StringBuffer contextbuf = new StringBuffer("\n");
218:
219: contextbuf.append(" >>>> ContextResourcen <<<<\n");
220: for (Iterator<ContextResource> i = crm
221: .getResourceIterator(); i.hasNext();) {
222: ContextResource res = (ContextResource) i.next();
223: contextbuf.append(" "
224: + res.getClass().getName() + ": ");
225: contextbuf.append(res.toString() + "\n");
226: }
227:
228: return contextbuf.toString();
229: }
230:
231: public void markSessionForCleanup() {
232: this .session.setAttribute(
233: AbstractXMLServlet.SESS_CLEANUP_FLAG_STAGE1, true);
234: }
235:
236: public void addSessionStatusListener(SessionStatusListener l) {
237: synchronized (this .sessionEndNotificator) {
238: if (!sessionEndNotificator.sessionListeners.contains(l)) {
239: sessionEndNotificator.sessionListeners.add(l);
240: }
241: }
242: }
243:
244: public void removeSessionStatusListener(SessionStatusListener l) {
245: synchronized (this .sessionEndNotificator) {
246: sessionEndNotificator.sessionListeners.remove(l);
247: }
248: }
249: }
250:
251: private SessionContextImpl sessioncontext;
252: private ServerContextImpl servercontext;
253: private ThreadLocal<RequestContextImpl> requestcontextstore = new ThreadLocal<RequestContextImpl>();
254:
255: public ContextImpl(ServerContextImpl servercontext,
256: HttpSession session) throws PustefixApplicationException,
257: PustefixCoreException {
258: this .servercontext = servercontext;
259: this .sessioncontext = new SessionContextImpl(session);
260: sessioncontext.init(this );
261: }
262:
263: public void addCookie(Cookie cookie) {
264: getRequestContextForCurrentThreadWithError().addCookie(cookie);
265: }
266:
267: public void addPageMessage(StatusCode scode) {
268: getRequestContextForCurrentThreadWithError().addPageMessage(
269: scode);
270: }
271:
272: public void addPageMessage(StatusCode scode, String level) {
273: getRequestContextForCurrentThreadWithError().addPageMessage(
274: scode, level);
275: }
276:
277: public void addPageMessage(StatusCode scode, String[] args) {
278: getRequestContextForCurrentThreadWithError().addPageMessage(
279: scode, args);
280: }
281:
282: public void addPageMessage(StatusCode scode, String[] args,
283: String level) {
284: getRequestContextForCurrentThreadWithError().addPageMessage(
285: scode, args, level);
286: }
287:
288: public boolean finalPageIsRunning() {
289: return getRequestContextForCurrentThreadWithError()
290: .finalPageIsRunning();
291: }
292:
293: public boolean flowIsRunning() {
294: return getRequestContextForCurrentThreadWithError()
295: .flowIsRunning();
296: }
297:
298: public boolean precedingFlowNeedsData()
299: throws PustefixApplicationException {
300: return getRequestContextForCurrentThreadWithError()
301: .precedingFlowNeedsData();
302: }
303:
304: public PageRequestConfig getConfigForCurrentPageRequest() {
305: return getRequestContextForCurrentThreadWithError()
306: .getConfigForCurrentPageRequest();
307: }
308:
309: public ContextConfig getContextConfig() {
310: return getServerContext().getContextConfig();
311: }
312:
313: public ContextResourceManager getContextResourceManager() {
314: return sessioncontext.getContextResourceManager();
315: }
316:
317: public PageFlow getCurrentPageFlow() {
318: return getRequestContextForCurrentThreadWithError()
319: .getCurrentPageFlow();
320: }
321:
322: public PageRequest getCurrentPageRequest() {
323: return getRequestContextForCurrentThreadWithError()
324: .getCurrentPageRequest();
325: }
326:
327: public String getLanguage() {
328: return getRequestContextForCurrentThreadWithError()
329: .getLanguage();
330: }
331:
332: public String getSessionLanguage() {
333: return sessioncontext.getLanguage();
334: }
335:
336: public Throwable getLastException() {
337: return getRequestContextForCurrentThreadWithError()
338: .getLastException();
339: }
340:
341: public String getName() {
342: return getServerContext().getName();
343: }
344:
345: public Properties getProperties() {
346: return getServerContext().getProperties();
347: }
348:
349: public Properties getPropertiesForContextResource(
350: ContextResource res) {
351: return getServerContext().getPropertiesForContextResource(res);
352: }
353:
354: public Properties getPropertiesForCurrentPageRequest() {
355: return getRequestContextForCurrentThreadWithError()
356: .getPropertiesForCurrentPageRequest();
357: }
358:
359: public Cookie[] getRequestCookies() {
360: return getRequestContextForCurrentThreadWithError()
361: .getRequestCookies();
362: }
363:
364: public Variant getVariant() {
365: return getRequestContextForCurrentThreadWithError()
366: .getVariant();
367: }
368:
369: public Variant getSessionVariant() {
370: return sessioncontext.getVariant();
371: }
372:
373: public String getVisitId() {
374: return sessioncontext.getVisitId();
375: }
376:
377: public boolean isCurrentPageFlowRequestedByUser() {
378: return getRequestContextForCurrentThreadWithError()
379: .isCurrentPageFlowRequestedByUser();
380: }
381:
382: public boolean isCurrentPageRequestInCurrentFlow() {
383: return getRequestContextForCurrentThreadWithError()
384: .isCurrentPageRequestInCurrentFlow();
385: }
386:
387: public boolean isJumpToPageFlowSet() {
388: return getRequestContextForCurrentThreadWithError()
389: .isJumpToPageFlowSet();
390: }
391:
392: public boolean isJumpToPageSet() {
393: return getRequestContextForCurrentThreadWithError()
394: .isJumpToPageSet();
395: }
396:
397: public boolean isProhibitContinueSet() {
398: return getRequestContextForCurrentThreadWithError()
399: .isProhibitContinueSet();
400: }
401:
402: public boolean jumpToPageIsRunning() {
403: return getRequestContextForCurrentThreadWithError()
404: .jumpToPageIsRunning();
405: }
406:
407: public void prohibitContinue() {
408: getRequestContextForCurrentThreadWithError().prohibitContinue();
409: }
410:
411: public void setCurrentPageFlow(String pageflow) {
412: getRequestContextForCurrentThreadWithError()
413: .setCurrentPageFlow(pageflow);
414: }
415:
416: public void setJumpToPage(String pagename) {
417: getRequestContextForCurrentThreadWithError().setJumpToPage(
418: pagename);
419: }
420:
421: public void setJumpToPageFlow(String pageflow) {
422: getRequestContextForCurrentThreadWithError().setJumpToPageFlow(
423: pageflow);
424: }
425:
426: public void setLanguage(String lang) {
427: getRequestContextForCurrentThreadWithError().setLanguage(lang);
428: sessioncontext.setLanguage(lang);
429: }
430:
431: public void setVariant(Variant variant) {
432: getRequestContextForCurrentThreadWithError()
433: .setVariantForThisRequestOnly(variant);
434: sessioncontext.setVariant(variant);
435: }
436:
437: public void setVariantForThisRequestOnly(Variant variant) {
438: getRequestContextForCurrentThreadWithError()
439: .setVariantForThisRequestOnly(variant);
440: }
441:
442: public boolean stateMustSupplyFullDocument() {
443: return getRequestContextForCurrentThreadWithError()
444: .stateMustSupplyFullDocument();
445: }
446:
447: public void forceStopAtNextStep(boolean forcestop) {
448: getRequestContextForCurrentThreadWithError()
449: .forceStopAtNextStep(forcestop);
450: }
451:
452: public boolean isPageAccessible(String pagename) throws Exception {
453: RequestContextImpl requestcontext = getRequestContextForCurrentThreadWithError();
454: if (getContextConfig().isSynchronized()) {
455: synchronized (this ) {
456: return requestcontext.isPageAccessible(pagename);
457: }
458: } else {
459: return requestcontext.isPageAccessible(pagename);
460: }
461: }
462:
463: public boolean isPageAlreadyVisited(String pagename)
464: throws Exception {
465: return sessioncontext.isVisitedPage(pagename);
466: }
467:
468: public void addVisitedPage(String pagename) {
469: sessioncontext.addVisitedPage(pagename);
470: }
471:
472: // ----------------
473:
474: public void setServerContext(ServerContextImpl servercontext) {
475: // Update current configuration
476: this .servercontext = servercontext;
477: }
478:
479: public boolean isAuthorized() throws Exception {
480: return (this .getRequestContextForCurrentThreadWithError()
481: .checkAuthorization(false, false) == null);
482: }
483:
484: public void prepareForRequest() {
485: // This allows to use OLDER servercontexts during requests
486: requestcontextstore.set(new RequestContextImpl(servercontext,
487: this ));
488: }
489:
490: public SPDocument handleRequest(PfixServletRequest preq)
491: throws PustefixApplicationException, PustefixCoreException {
492: if (getContextConfig().isSynchronized()) {
493: synchronized (this ) {
494: return getRequestContextForCurrentThreadWithError()
495: .handleRequest(preq);
496: }
497: } else {
498: return getRequestContextForCurrentThreadWithError()
499: .handleRequest(preq);
500: }
501: }
502:
503: public void cleanupAfterRequest() {
504: requestcontextstore.set(null);
505: }
506:
507: // Used by TransformerCallback to set the right RequestContextImpl when
508: // rendering a page
509: public void setRequestContextForCurrentThread(
510: RequestContextImpl requestcontext) {
511: requestcontextstore.set(requestcontext);
512: }
513:
514: public void invalidateToken(String token) {
515: sessioncontext.invalidateToken(token);
516: }
517:
518: public String getToken(String tokenName) {
519: return sessioncontext.getToken(tokenName);
520: }
521:
522: public boolean isValidToken(String tokenName, String token) {
523: return sessioncontext.isValidToken(tokenName, token);
524: }
525:
526: public Authentication getAuthentication() {
527: return sessioncontext.getAuthentication();
528: }
529:
530: public void setAuthentication(Authentication authentication) {
531: sessioncontext.setAuthentication(authentication);
532: }
533:
534: @Override
535: public String toString() {
536: RequestContextImpl requestcontext = getRequestContextForCurrentThread();
537: if (requestcontext != null) {
538: return requestcontext.toString()
539: + sessioncontext.toString();
540: } else {
541: return sessioncontext.toString();
542: }
543: }
544:
545: // --------------
546:
547: private RequestContextImpl getRequestContextForCurrentThread() {
548: return requestcontextstore.get();
549: }
550:
551: private RequestContextImpl getRequestContextForCurrentThreadWithError() {
552: RequestContextImpl requestcontext = requestcontextstore.get();
553: if (requestcontext == null) {
554: throw new IllegalStateException(
555: "Request object is not available for current thread");
556: }
557: return requestcontext;
558: }
559:
560: private ServerContextImpl getServerContext() {
561: RequestContextImpl requestcontext = getRequestContextForCurrentThread();
562: if (requestcontext != null) {
563: return requestcontext.getServerContext();
564: } else {
565: return servercontext;
566: }
567: }
568:
569: public void markSessionForCleanup() {
570: this .sessioncontext.markSessionForCleanup();
571: }
572:
573: public void addSessionStatusListener(SessionStatusListener l) {
574: this .sessioncontext.addSessionStatusListener(l);
575: }
576:
577: public void removeSessionStatusListener(SessionStatusListener l) {
578: this .sessioncontext.removeSessionStatusListener(l);
579: }
580:
581: // Notification on session binding / unbinding
582:
583: public void valueBound(HttpSessionBindingEvent ev) {
584: this .sessioncontext.session = ev.getSession();
585: }
586:
587: public void valueUnbound(HttpSessionBindingEvent ev) {
588: if (ev.getSession() == this.sessioncontext.session) {
589: this.sessioncontext.session = null;
590: }
591: }
592:
593: }
|