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.portal.layout.impl;
018:
019: import java.util.ArrayList;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.apache.avalon.framework.CascadingRuntimeException;
026: import org.apache.avalon.framework.activity.Disposable;
027: import org.apache.avalon.framework.activity.Initializable;
028: import org.apache.avalon.framework.component.Component;
029: import org.apache.avalon.framework.configuration.Configurable;
030: import org.apache.avalon.framework.configuration.Configuration;
031: import org.apache.avalon.framework.configuration.ConfigurationException;
032: import org.apache.avalon.framework.logger.AbstractLogEnabled;
033: import org.apache.avalon.framework.service.ServiceException;
034: import org.apache.avalon.framework.service.ServiceManager;
035: import org.apache.avalon.framework.service.ServiceSelector;
036: import org.apache.avalon.framework.service.Serviceable;
037: import org.apache.avalon.framework.thread.ThreadSafe;
038: import org.apache.cocoon.ProcessingException;
039: import org.apache.cocoon.portal.PortalComponentManager;
040: import org.apache.cocoon.portal.PortalService;
041: import org.apache.cocoon.portal.aspect.AspectDataHandler;
042: import org.apache.cocoon.portal.aspect.AspectDataStore;
043: import org.apache.cocoon.portal.aspect.AspectDescription;
044: import org.apache.cocoon.portal.aspect.impl.DefaultAspectDataHandler;
045: import org.apache.cocoon.portal.aspect.impl.DefaultAspectDescription;
046: import org.apache.cocoon.portal.coplet.CopletFactory;
047: import org.apache.cocoon.portal.event.Event;
048: import org.apache.cocoon.portal.event.EventManager;
049: import org.apache.cocoon.portal.event.LayoutEvent;
050: import org.apache.cocoon.portal.event.Receiver;
051: import org.apache.cocoon.portal.event.impl.FullScreenCopletEvent;
052: import org.apache.cocoon.portal.event.impl.LayoutRemoveEvent;
053: import org.apache.cocoon.portal.layout.CompositeLayout;
054: import org.apache.cocoon.portal.layout.Item;
055: import org.apache.cocoon.portal.layout.Layout;
056: import org.apache.cocoon.portal.layout.LayoutFactory;
057: import org.apache.cocoon.portal.layout.renderer.Renderer;
058: import org.apache.cocoon.portal.profile.ProfileManager;
059: import org.apache.cocoon.util.ClassUtils;
060:
061: /**
062: *
063: * <h2>Configuration</h2>
064: * <table><tbody>
065: * <tr><th>layouts</th>
066: * <td>List of layouts.</td>
067: * <td>req</td>
068: * <td>Configuration</td>
069: * <td><code>null</code></td>
070: * </tr>
071: * <tr>
072: * <th>layouts/layout</th>
073: * <td>Multiple configured layouts.
074: * </td>
075: * <td>req</td>
076: * <td>Configuration</td>
077: * <td><code>null</code></td>
078: * </tr>
079: * <tr>
080: * <th>layouts/layout/attribute::name</th>
081: * <td>Unique layout name.</td>
082: * <td>req</td>
083: * <td>String</td>
084: * <td><code>null</code></td>
085: * </tr>
086: * <tr>
087: * <th>layouts/layout/attribute::create-id</th>
088: * <td></td>
089: * <td></td>
090: * <td>boolean</td>
091: * <td><code>false</code></td>
092: * </tr>
093: * <tr>
094: * <th>layouts/layout/renderers/attribute::default</th>
095: * <td></td>
096: * <td>req</td>
097: * <td>String</td>
098: * <td><code>null</code></td>
099: * </tr>
100: * <tr>
101: * <th>layouts/layout/renderers/renderer</th>
102: * <td></td>
103: * <td>req</td>
104: * <td>Configuration</td>
105: * <td><code>null</code></td>
106: * </tr>
107: * <tr>
108: * <th>layouts/layout/renderers/renderer/attribute::name</th>
109: * <td></td>
110: * <td>req</td>
111: * <td>String</td>
112: * <td><code>null</code></td>
113: * </tr>
114: * <tr>
115: * <th>layouts/layout/aspects/aspect</th>
116: * <td></td>
117: * <td>req</td>
118: * <td>String</td>
119: * <td><code>null</code></td>
120: * </tr>
121: * </tbody></table>
122: *
123: * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
124: * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a>
125: *
126: * @version CVS $Id: DefaultLayoutFactory.java 433543 2006-08-22 06:22:54Z crossley $
127: */
128: public class DefaultLayoutFactory extends AbstractLogEnabled implements
129: ThreadSafe, Component, LayoutFactory, Configurable, Disposable,
130: Serviceable, Initializable, Receiver {
131:
132: protected Map layouts = new HashMap();
133:
134: protected List descriptions = new ArrayList();
135:
136: protected ServiceSelector storeSelector;
137:
138: protected ServiceManager manager;
139:
140: protected Configuration[] layoutsConf;
141:
142: protected static long idCounter = System.currentTimeMillis();
143:
144: /* (non-Javadoc)
145: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
146: */
147: public void service(ServiceManager manager) throws ServiceException {
148: this .manager = manager;
149: this .storeSelector = (ServiceSelector) this .manager
150: .lookup(AspectDataStore.ROLE + "Selector");
151: }
152:
153: /**
154: * Configure a layout
155: */
156: protected void configureLayout(Configuration layoutConf)
157: throws ConfigurationException {
158: DefaultLayoutDescription desc = new DefaultLayoutDescription();
159: final String name = layoutConf.getAttribute("name");
160:
161: // unique test
162: if (this .layouts.get(name) != null) {
163: throw new ConfigurationException(
164: "Layout name must be unique. Double definition for "
165: + name);
166: }
167: desc.setName(name);
168: desc.setClassName(layoutConf.getAttribute("class"));
169: desc.setCreateId(layoutConf.getAttributeAsBoolean("create-id",
170: false));
171: desc.setItemClassName(layoutConf.getAttribute("item-class",
172: null));
173:
174: // the renderers
175: final String defaultRenderer = layoutConf.getChild("renderers")
176: .getAttribute("default");
177: desc.setDefaultRendererName(defaultRenderer);
178:
179: final Configuration[] rendererConfs = layoutConf.getChild(
180: "renderers").getChildren("renderer");
181: if (rendererConfs != null) {
182: boolean found = false;
183: for (int m = 0; m < rendererConfs.length; m++) {
184: final String rName = rendererConfs[m]
185: .getAttribute("name");
186: desc.addRendererName(rName);
187: if (defaultRenderer.equals(rName)) {
188: found = true;
189: }
190: }
191: if (!found) {
192: throw new ConfigurationException("Default renderer '"
193: + defaultRenderer
194: + "' is not configured for layout '" + name
195: + "'");
196: }
197: } else {
198: throw new ConfigurationException("Default renderer '"
199: + defaultRenderer
200: + "' is not configured for layout '" + name + "'");
201: }
202:
203: // and now the aspects
204: final Configuration[] aspectsConf = layoutConf.getChild(
205: "aspects").getChildren("aspect");
206: if (aspectsConf != null) {
207: for (int m = 0; m < aspectsConf.length; m++) {
208: AspectDescription adesc = DefaultAspectDescription
209: .newInstance(aspectsConf[m]);
210: desc.addAspectDescription(adesc);
211: }
212: }
213: // now query all configured renderers for their aspects
214: PortalService service = null;
215: try {
216: service = (PortalService) this .manager
217: .lookup(PortalService.ROLE);
218: PortalComponentManager pcManager = service
219: .getComponentManager();
220:
221: Iterator rendererIterator = desc.getRendererNames();
222: while (rendererIterator.hasNext()) {
223: final String rendererName = (String) rendererIterator
224: .next();
225: Renderer renderer = pcManager.getRenderer(rendererName);
226:
227: Iterator aspectIterator = renderer
228: .getAspectDescriptions();
229: while (aspectIterator.hasNext()) {
230: final AspectDescription adesc = (AspectDescription) aspectIterator
231: .next();
232: desc.addAspectDescription(adesc);
233: }
234: }
235: } catch (ServiceException ce) {
236: throw new ConfigurationException(
237: "Unable to lookup renderer selector.", ce);
238: } finally {
239: this .manager.release(service);
240: }
241:
242: // set the aspect data handler
243: DefaultAspectDataHandler handler = new DefaultAspectDataHandler(
244: desc, this .storeSelector);
245: this .layouts
246: .put(desc.getName(), new Object[] { desc, handler });
247: this .descriptions.add(desc);
248: }
249:
250: /* (non-Javadoc)
251: * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
252: */
253: public void configure(Configuration configuration)
254: throws ConfigurationException {
255: this .layoutsConf = configuration.getChild("layouts")
256: .getChildren("layout");
257: }
258:
259: protected void init() {
260: // FIXME when we switch to another container we can remove
261: // the lazy evaluation
262: if (this .layoutsConf != null) {
263: synchronized (this ) {
264: if (this .layoutsConf != null) {
265: for (int i = 0; i < layoutsConf.length; i++) {
266: try {
267: this .configureLayout(layoutsConf[i]);
268: } catch (ConfigurationException ce) {
269: throw new CascadingRuntimeException(
270: "Unable to configure layout.", ce);
271: }
272: }
273: this .layoutsConf = null;
274: }
275: }
276: }
277: }
278:
279: /* (non-Javadoc)
280: * @see org.apache.cocoon.portal.layout.LayoutFactory#prepareLayout(org.apache.cocoon.portal.layout.Layout)
281: */
282: public void prepareLayout(Layout layout) throws ProcessingException {
283: if (layout != null) {
284:
285: this .init();
286:
287: final String layoutName = layout.getName();
288: if (layoutName == null) {
289: throw new ProcessingException("Layout '"
290: + layout.getId() + "' has no associated name.");
291: }
292: Object[] o = (Object[]) this .layouts.get(layoutName);
293:
294: if (o == null) {
295: throw new ProcessingException(
296: "LayoutDescription with name '" + layoutName
297: + "' not found.");
298: }
299: DefaultLayoutDescription layoutDescription = (DefaultLayoutDescription) o[0];
300:
301: layout.setDescription(layoutDescription);
302: layout.setAspectDataHandler((AspectDataHandler) o[1]);
303:
304: // recursive
305: if (layout instanceof CompositeLayout) {
306: CompositeLayout composite = (CompositeLayout) layout;
307: composite.setItemClassName(layoutDescription
308: .getItemClassName());
309:
310: Iterator items = composite.getItems().iterator();
311: while (items.hasNext()) {
312: this .prepareLayout(((Item) items.next())
313: .getLayout());
314: }
315: }
316: }
317: }
318:
319: /* (non-Javadoc)
320: * @see org.apache.cocoon.portal.layout.LayoutFactory#newInstance(java.lang.String)
321: */
322: public Layout newInstance(String layoutName)
323: throws ProcessingException {
324: this .init();
325:
326: Object[] o = (Object[]) this .layouts.get(layoutName);
327:
328: if (o == null) {
329: throw new ProcessingException(
330: "LayoutDescription with name '" + layoutName
331: + "' not found.");
332: }
333: DefaultLayoutDescription layoutDescription = (DefaultLayoutDescription) o[0];
334:
335: Layout layout = null;
336: try {
337: Class clazz = ClassUtils.loadClass(layoutDescription
338: .getClassName());
339: layout = (Layout) clazz.newInstance();
340:
341: } catch (Exception e) {
342: throw new ProcessingException(
343: "Unable to create new instance", e);
344: }
345:
346: String id = null;
347: if (layoutDescription.createId()) {
348: synchronized (this ) {
349: id = layoutName + '_' + idCounter;
350: idCounter += 1;
351: }
352: }
353: layout.initialize(layoutName, id);
354: layout.setDescription(layoutDescription);
355: layout.setAspectDataHandler((AspectDataHandler) o[1]);
356:
357: if (layout instanceof CompositeLayout) {
358: CompositeLayout composite = (CompositeLayout) layout;
359: composite.setItemClassName(layoutDescription
360: .getItemClassName());
361: }
362:
363: PortalService service = null;
364: try {
365: service = (PortalService) this .manager
366: .lookup(PortalService.ROLE);
367: service.getComponentManager().getProfileManager().register(
368: layout);
369: } catch (ServiceException ce) {
370: throw new ProcessingException(
371: "Unable to lookup profile manager.", ce);
372: } finally {
373: this .manager.release(service);
374: }
375: return layout;
376: }
377:
378: /* (non-Javadoc)
379: * @see org.apache.cocoon.portal.layout.LayoutFactory#getLayoutDescriptions()
380: */
381: public List getLayoutDescriptions() {
382: this .init();
383: return this .descriptions;
384: }
385:
386: /* (non-Javadoc)
387: * @see org.apache.avalon.framework.activity.Disposable#dispose()
388: */
389: public void dispose() {
390: if (this .manager != null) {
391: EventManager eventManager = null;
392: try {
393: eventManager = (EventManager) this .manager
394: .lookup(EventManager.ROLE);
395: eventManager.unsubscribe(this );
396: } catch (Exception ignore) {
397: // ignore
398: } finally {
399: this .manager.release(eventManager);
400: }
401: this .manager.release(this .storeSelector);
402: this .storeSelector = null;
403: this .manager = null;
404: }
405:
406: }
407:
408: /* (non-Javadoc)
409: * @see org.apache.avalon.framework.activity.Initializable#initialize()
410: */
411: public void initialize() throws Exception {
412: EventManager eventManager = null;
413: try {
414: eventManager = (EventManager) this .manager
415: .lookup(EventManager.ROLE);
416: eventManager.subscribe(this );
417: } finally {
418: this .manager.release(eventManager);
419: }
420: }
421:
422: /**
423: * @see Receiver
424: */
425: public void inform(LayoutEvent event, PortalService service) {
426: Layout layout = (Layout) event.getTarget();
427: if (event instanceof LayoutRemoveEvent) {
428: try {
429: this .remove(layout);
430: } catch (ProcessingException pe) {
431: throw new CascadingRuntimeException(
432: "Exception during removal.", pe);
433: }
434: }
435: }
436:
437: /* (non-Javadoc)
438: * @see org.apache.cocoon.portal.layout.LayoutFactory#remove(org.apache.cocoon.portal.layout.Layout)
439: */
440: public void remove(Layout layout) throws ProcessingException {
441: if (layout != null) {
442: this .init();
443: if (layout instanceof CompositeLayout) {
444: final CompositeLayout cl = (CompositeLayout) layout;
445: while (cl.getItems().size() > 0) {
446: final Item i = cl.getItem(0);
447: this .remove(i.getLayout());
448: }
449: }
450: Item parent = layout.getParent();
451: if (parent != null && parent.getParent() != null) {
452: parent.getParent().removeItem(parent);
453: }
454:
455: PortalService service = null;
456: EventManager eventManager = null;
457: try {
458: service = (PortalService) this .manager
459: .lookup(PortalService.ROLE);
460: ProfileManager profileManager = service
461: .getComponentManager().getProfileManager();
462: if (layout instanceof CopletLayout) {
463: // full screen?
464: if (layout.equals(service.getEntryLayout(null))) {
465: Event event = new FullScreenCopletEvent(
466: ((CopletLayout) layout)
467: .getCopletInstanceData(), null);
468: eventManager = (EventManager) this .manager
469: .lookup(EventManager.ROLE);
470: eventManager.send(event);
471: service.getComponentManager().getLinkService()
472: .addEventToLink(event);
473: }
474: CopletFactory factory = service
475: .getComponentManager().getCopletFactory();
476: factory.remove(((CopletLayout) layout)
477: .getCopletInstanceData());
478: }
479: profileManager.unregister(layout);
480: } catch (ServiceException ce) {
481: throw new ProcessingException(
482: "Unable to lookup portal service.", ce);
483: } finally {
484: this.manager.release(service);
485: this.manager.release(eventManager);
486: }
487: }
488: }
489: }
|