001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.webapps.session.components;
018:
019: import org.apache.avalon.framework.activity.Disposable;
020: import org.apache.avalon.framework.component.Component;
021: import org.apache.avalon.framework.context.Context;
022: import org.apache.avalon.framework.context.ContextException;
023: import org.apache.avalon.framework.context.Contextualizable;
024: import org.apache.avalon.framework.logger.AbstractLogEnabled;
025: import org.apache.avalon.framework.service.ServiceException;
026: import org.apache.avalon.framework.service.ServiceManager;
027: import org.apache.avalon.framework.service.Serviceable;
028: import org.apache.avalon.framework.thread.ThreadSafe;
029: import org.apache.cocoon.ProcessingException;
030: import org.apache.cocoon.components.ContextHelper;
031: import org.apache.cocoon.environment.Request;
032: import org.apache.cocoon.environment.Session;
033: import org.apache.cocoon.webapps.session.ContextManager;
034: import org.apache.cocoon.webapps.session.SessionConstants;
035: import org.apache.cocoon.webapps.session.SessionManager;
036: import org.apache.cocoon.webapps.session.context.SessionContext;
037: import org.apache.cocoon.xml.XMLConsumer;
038: import org.apache.cocoon.xml.XMLUtils;
039: import org.apache.cocoon.xml.dom.DOMUtil;
040: import org.w3c.dom.DocumentFragment;
041: import org.w3c.dom.Element;
042: import org.w3c.dom.Node;
043: import org.w3c.dom.NodeList;
044: import org.xml.sax.SAXException;
045:
046: /**
047: * This is the default implementation of the session manager
048: *
049: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
050: * @deprecated This block is deprecated and will be removed in future versions.
051: * @version CVS $Id: DefaultSessionManager.java 433543 2006-08-22 06:22:54Z crossley $
052: */
053: public final class DefaultSessionManager extends AbstractLogEnabled
054: implements Serviceable, Component, ThreadSafe, SessionManager,
055: Disposable, Contextualizable {
056:
057: /** The context */
058: private Context context;
059:
060: /** The <code>ServiceManager</code> */
061: private ServiceManager manager;
062:
063: /** The context manager */
064: private ContextManager contextManager;
065:
066: /**
067: * Avalon Serviceable Interface
068: */
069: public void service(ServiceManager manager) throws ServiceException {
070: this .manager = manager;
071: this .contextManager = (ContextManager) this .manager
072: .lookup(ContextManager.ROLE);
073: }
074:
075: /**
076: * Avalon Disposable Interface
077: */
078: public void dispose() {
079: if (this .manager != null) {
080: this .manager.release(this .contextManager);
081: this .manager = null;
082: this .contextManager = null;
083: }
084: }
085:
086: /**
087: * Create a new session for the user.
088: * A new session is created for this user. If the user has already a session,
089: * no new session is created and the old one is returned.
090: */
091: public Session createSession() {
092: // synchronized
093: if (this .getLogger().isDebugEnabled()) {
094: this .getLogger().debug("BEGIN createSession");
095: }
096: Session session = this .getSession(true);
097:
098: if (this .getLogger().isDebugEnabled()) {
099: this .getLogger().debug(
100: "END createSession session=" + session);
101: }
102: return session;
103: }
104:
105: /**
106: * Get the session for the current user.
107: * If the user has no session right now, <CODE>null</CODE> is returned.
108: * If createFlag is true, the session is created if it does not exist.
109: */
110: public Session getSession(boolean createFlag) {
111: final Request request = ContextHelper.getRequest(this .context);
112:
113: // synchronized
114: if (this .getLogger().isDebugEnabled()) {
115: this .getLogger().debug(
116: "BEGIN getSession create=" + createFlag);
117: }
118: Session session = request.getSession(createFlag);
119:
120: if (this .getLogger().isDebugEnabled()) {
121: this .getLogger().debug("END getSession session=" + session);
122: }
123:
124: return session;
125: }
126:
127: /**
128: * Terminate the current session.
129: * If the user has a session, this session is terminated and all of its
130: * data is deleted.
131: * @param force If this is set to true the session is terminated, if
132: * it is set to false, the session is only terminated
133: * if no session context is available.
134: */
135: public void terminateSession(boolean force)
136: throws ProcessingException {
137: // synchronized
138: if (this .getLogger().isDebugEnabled()) {
139: this .getLogger().debug(
140: "BEGIN terminateSession force=" + force);
141: }
142:
143: Session session = this .getSession(false);
144: if (session != null) {
145: if (force || this .contextManager.hasSessionContext()) {
146: synchronized (session) {
147: session.invalidate();
148: }
149: }
150: }
151: if (this .getLogger().isDebugEnabled()) {
152: this .getLogger().debug("END terminateSession");
153: }
154: }
155:
156: /**
157: * Get information from the context.
158: * A document fragment containg the xml data stored in the session context
159: * with the given name is returned. If the information is not available,
160: * <CODE>null</CODE> is returned.
161: * @param contextName The name of the public context.
162: * @param path XPath expression specifying which data to get.
163: * @return A DocumentFragment containing the data or <CODE>null</CODE>
164: */
165: public DocumentFragment getContextFragment(String contextName,
166: String path) throws ProcessingException {
167: // synchronized via context
168: if (this .getLogger().isDebugEnabled()) {
169: this .getLogger().debug(
170: "BEGIN getContextFragment name=" + contextName
171: + ", path=" + path);
172: }
173:
174: // test arguments
175: if (contextName == null) {
176: throw new ProcessingException(
177: "SessionManager.getContextFragment: Name is required");
178: }
179: if (path == null) {
180: throw new ProcessingException(
181: "SessionManager.getContextFragment: Path is required");
182: }
183:
184: SessionContext context = this .contextManager
185: .getContext(contextName);
186:
187: if (context == null) {
188: throw new ProcessingException(
189: "SessionManager.getContextFragment: Context '"
190: + contextName + "' not found.");
191: }
192:
193: DocumentFragment frag;
194: frag = context.getXML(path);
195:
196: if (this .getLogger().isDebugEnabled()) {
197: this
198: .getLogger()
199: .debug(
200: "END getContextFragment documentFragment="
201: + (frag == null ? "null"
202: : XMLUtils
203: .serializeNode(
204: frag,
205: XMLUtils
206: .createPropertiesForXML(false))));
207: }
208: return frag;
209: }
210:
211: /**
212: * Stream public context data.
213: * The document fragment containing the data from a path in the
214: * given context is streamed to the consumer.
215: *
216: * @param contextName The name of the public context.
217: * @param path XPath expression specifying which data to get.
218: *
219: * @return If the data is available <code>true</code> is returned,
220: * otherwise <code>false</code> is returned.
221: */
222: public boolean streamContextFragment(String contextName,
223: String path, XMLConsumer consumer) throws SAXException,
224: ProcessingException {
225: // synchronized via context
226: if (this .getLogger().isDebugEnabled()) {
227: this .getLogger().debug(
228: "BEGIN streamContextFragment name=" + contextName
229: + ", path=" + path + ", consumer"
230: + consumer);
231: }
232: boolean streamed = false;
233:
234: // test arguments
235: if (contextName == null) {
236: throw new ProcessingException(
237: "SessionManager.streamContextFragment: Name is required");
238: }
239: if (path == null) {
240: throw new ProcessingException(
241: "SessionManager.streamContextFragment: Path is required");
242: }
243:
244: SessionContext context = this .contextManager
245: .getContext(contextName);
246:
247: if (context == null) {
248: throw new ProcessingException(
249: "SessionManager.streamContextFragment: Context '"
250: + contextName + "' not found.");
251: }
252:
253: streamed = context.streamXML(path, consumer, consumer);
254:
255: if (this .getLogger().isDebugEnabled()) {
256: this .getLogger().debug(
257: "END streamContextFragment streamed=" + streamed);
258: }
259: return streamed;
260: }
261:
262: /**
263: * Set data in a public context.
264: * The document fragment containing the data is set at the given path in the
265: * public session context.
266: *
267: * @param contextName The name of the public context.
268: * @param path XPath expression specifying where to set the data.
269: * @param fragment The DocumentFragment containing the data.
270: *
271: */
272: public void setContextFragment(String contextName, String path,
273: DocumentFragment fragment) throws ProcessingException {
274: // synchronized via context
275:
276: if (this .getLogger().isDebugEnabled()) {
277: this
278: .getLogger()
279: .debug(
280: "BEGIN setContextFragment name="
281: + contextName
282: + ", path="
283: + path
284: + ", fragment="
285: + (fragment == null ? "null"
286: : XMLUtils
287: .serializeNode(
288: fragment,
289: XMLUtils
290: .createPropertiesForXML(false))));
291: }
292: // test arguments
293: if (contextName == null) {
294: throw new ProcessingException(
295: "SessionManager.setContextFragment: Name is required");
296: }
297: if (path == null) {
298: throw new ProcessingException(
299: "SessionManager.setContextFragment: Path is required");
300: }
301: if (fragment == null) {
302: throw new ProcessingException(
303: "SessionManager.setContextFragment: Fragment is required");
304: }
305:
306: // get context
307: SessionContext context = this .contextManager
308: .getContext(contextName);
309:
310: // check context
311: if (context == null) {
312: throw new ProcessingException(
313: "SessionManager.setContextFragment: Context '"
314: + contextName + "' not found.");
315: }
316:
317: context.setXML(path, fragment);
318:
319: if (this .getLogger().isDebugEnabled()) {
320: this .getLogger().debug("END setContextFragment");
321: }
322: }
323:
324: /**
325: * Append data in a public context.
326: * The document fragment containing the data is appended at the given
327: * path in the public session context.
328: *
329: * @param contextName The name of the public context.
330: * @param path XPath expression specifying where to append the data.
331: * @param fragment The DocumentFragment containing the data.
332: *
333: */
334: public void appendContextFragment(String contextName, String path,
335: DocumentFragment fragment) throws ProcessingException {
336: // synchronized via context
337: if (this .getLogger().isDebugEnabled()) {
338: this
339: .getLogger()
340: .debug(
341: "BEGIN appendContextFragment name="
342: + contextName
343: + ", path="
344: + path
345: + ", fragment="
346: + (fragment == null ? "null"
347: : XMLUtils
348: .serializeNode(
349: fragment,
350: XMLUtils
351: .createPropertiesForXML(false))));
352: }
353: // test arguments
354: if (contextName == null) {
355: throw new ProcessingException(
356: "SessionManager.appendContextFragment: Name is required");
357: }
358: if (path == null) {
359: throw new ProcessingException(
360: "SessionManager.appendContextFragment: Path is required");
361: }
362: if (fragment == null) {
363: throw new ProcessingException(
364: "SessionManager.appendContextFragment: Fragment is required");
365: }
366:
367: // get context
368: SessionContext context = this .contextManager
369: .getContext(contextName);
370:
371: // check context
372: if (context == null) {
373: throw new ProcessingException(
374: "SessionManager.appendContextFragment: Context '"
375: + contextName + "' not found.");
376: }
377:
378: context.appendXML(path, fragment);
379:
380: if (this .getLogger().isDebugEnabled()) {
381: this .getLogger().debug("END appendContextFragment");
382: }
383: }
384:
385: /**
386: * Merge data in a public context.
387: * The document fragment containing the data is merged at the given
388: * path in the public session context.
389: *
390: * @param contextName The name of the public context.
391: * @param path XPath expression specifying where to merge the data.
392: * @param fragment The DocumentFragment containing the data.
393: *
394: */
395: public void mergeContextFragment(String contextName, String path,
396: DocumentFragment fragment) throws ProcessingException {
397: // synchronized via context
398: if (this .getLogger().isDebugEnabled()) {
399: this
400: .getLogger()
401: .debug(
402: "BEGIN mergeContextFragment name="
403: + contextName
404: + ", path="
405: + path
406: + ", fragment="
407: + (fragment == null ? "null"
408: : XMLUtils
409: .serializeNode(
410: fragment,
411: XMLUtils
412: .createPropertiesForXML(false))));
413: }
414:
415: // test arguments
416: if (contextName == null) {
417: throw new ProcessingException(
418: "SessionManager.mergeContextFragment: Name is required");
419: }
420: if (path == null) {
421: throw new ProcessingException(
422: "SessionManager.mergeContextFragment: Path is required");
423: }
424: if (fragment == null) {
425: throw new ProcessingException(
426: "SessionManager.mergeContextFragment: Fragment is required");
427: }
428:
429: // get context
430: SessionContext context = this .contextManager
431: .getContext(contextName);
432:
433: // check context
434: if (context == null) {
435: throw new ProcessingException(
436: "SessionManager.mergeContextFragment: Context '"
437: + contextName + "' not found.");
438: }
439:
440: Node contextNode = context.getSingleNode(path);
441: if (contextNode == null) {
442: // no merge required
443: context.setXML(path, fragment);
444: } else {
445: this .importNode(contextNode, fragment, false);
446: context.setNode(path, contextNode);
447: }
448:
449: if (this .getLogger().isDebugEnabled()) {
450: this .getLogger().debug("END mergeContextFragment");
451: }
452: }
453:
454: /**
455: * Remove data in a public context.
456: * The data specified by the path is removed from the public session context.
457: *
458: * @param contextName The name of the public context.
459: * @param path XPath expression specifying where to merge the data.
460: *
461: */
462: public void removeContextFragment(String contextName, String path)
463: throws ProcessingException {
464: // synchronized via context
465: if (this .getLogger().isDebugEnabled()) {
466: this .getLogger().debug(
467: "BEGIN removeContextFragment name=" + contextName
468: + ", path=" + path);
469: }
470: // test arguments
471: if (contextName == null) {
472: throw new ProcessingException(
473: "SessionManager.removeContextFragment: Name is required");
474: }
475: if (path == null) {
476: throw new ProcessingException(
477: "SessionManager.removeContextFragment: Path is required");
478: }
479:
480: // get context
481: SessionContext context = this .contextManager
482: .getContext(contextName);
483:
484: // check context
485: if (context == null) {
486: throw new ProcessingException(
487: "SessionManager.removeContextFragment: Context '"
488: + contextName + "' not found.");
489: }
490:
491: context.removeXML(path);
492:
493: if (this .getLogger().isDebugEnabled()) {
494: this .getLogger().debug("END removeContextFragment");
495: }
496: }
497:
498: /**
499: * Import nodes. If preserve is set to true, the nodes
500: * marked with cocoon:preserve are always imported
501: * overwriting others!
502: */
503: private void importNode(Node profile, Node delta, boolean preserve) {
504: // no sync req
505: NodeList profileChilds = null;
506: NodeList deltaChilds = delta.getChildNodes();
507: int i, len;
508: int m, l;
509: boolean found;
510: Node currentDelta = null;
511: Node currentProfile = null;
512:
513: len = deltaChilds.getLength();
514: for (i = 0; i < len; i++) {
515: currentDelta = deltaChilds.item(i);
516: if (currentDelta.getNodeType() == Node.ELEMENT_NODE) {
517: // search the delta node in the profile
518: profileChilds = profile.getChildNodes();
519: l = profileChilds.getLength();
520: m = 0;
521: found = false;
522: while (found == false && m < l) {
523: currentProfile = profileChilds.item(m);
524: if (currentProfile.getNodeType() == Node.ELEMENT_NODE
525: && currentProfile.getNodeName().equals(
526: currentDelta.getNodeName()) == true) {
527:
528: // now we have found a node with the same name
529: // next: the attributes must match also
530: found = DOMUtil.compareAttributes(
531: (Element) currentProfile,
532: (Element) currentDelta);
533: }
534: if (found == false)
535: m++;
536: }
537: if (found == true) {
538: // this is not new
539:
540: if (preserve == true
541: && ((Element) currentDelta)
542: .hasAttributeNS(
543: SessionConstants.SESSION_NAMESPACE_URI,
544: "preserve")
545: && ((Element) currentDelta)
546: .getAttributeNS(
547: SessionConstants.SESSION_NAMESPACE_URI,
548: "preserve")
549: .equalsIgnoreCase("true")) {
550: // replace the original with the delta
551: profile.replaceChild(profile.getOwnerDocument()
552: .importNode(currentDelta, true),
553: currentProfile);
554: } else {
555: // do we have elements as children or text?
556: if (currentDelta.hasChildNodes() == true) {
557: currentDelta.normalize();
558: currentProfile.normalize();
559: // do a recursive call for sub elements
560: this .importNode(currentProfile,
561: currentDelta, preserve);
562: // and now the text nodes: Remove all from the profile and add all
563: // of the delta
564: NodeList childs = currentProfile
565: .getChildNodes();
566: int index, max;
567: max = childs.getLength();
568: for (index = max - 1; index >= 0; index--) {
569: if (childs.item(index).getNodeType() == Node.TEXT_NODE) {
570: currentProfile.removeChild(childs
571: .item(index));
572: }
573: }
574: childs = currentDelta.getChildNodes();
575: max = childs.getLength();
576: for (index = 0; index < max; index++) {
577: if (childs.item(index).getNodeType() == Node.TEXT_NODE) {
578: currentProfile
579: .appendChild(currentProfile
580: .getOwnerDocument()
581: .createTextNode(
582: childs
583: .item(
584: index)
585: .getNodeValue()));
586: }
587: }
588: }
589: }
590: } else {
591: profile.appendChild(profile.getOwnerDocument()
592: .importNode(currentDelta, true));
593: }
594: }
595:
596: }
597:
598: }
599:
600: /* (non-Javadoc)
601: * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
602: */
603: public void contextualize(Context context) throws ContextException {
604: this.context = context;
605: }
606:
607: }
|