/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.portal.layout.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.avalon.framework.CascadingRuntimeException; import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.component.Component; import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.ServiceSelector; import org.apache.avalon.framework.service.Serviceable; import org.apache.avalon.framework.thread.ThreadSafe; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.portal.PortalComponentManager; import org.apache.cocoon.portal.PortalService; import org.apache.cocoon.portal.aspect.AspectDataHandler; import org.apache.cocoon.portal.aspect.AspectDataStore; import org.apache.cocoon.portal.aspect.AspectDescription; import org.apache.cocoon.portal.aspect.impl.DefaultAspectDataHandler; import org.apache.cocoon.portal.aspect.impl.DefaultAspectDescription; import org.apache.cocoon.portal.coplet.CopletFactory; import org.apache.cocoon.portal.event.Event; import org.apache.cocoon.portal.event.EventManager; import org.apache.cocoon.portal.event.LayoutEvent; import org.apache.cocoon.portal.event.Receiver; import org.apache.cocoon.portal.event.impl.FullScreenCopletEvent; import org.apache.cocoon.portal.event.impl.LayoutRemoveEvent; import org.apache.cocoon.portal.layout.CompositeLayout; import org.apache.cocoon.portal.layout.Item; import org.apache.cocoon.portal.layout.Layout; import org.apache.cocoon.portal.layout.LayoutFactory; import org.apache.cocoon.portal.layout.renderer.Renderer; import org.apache.cocoon.portal.profile.ProfileManager; import org.apache.cocoon.util.ClassUtils; /** * * <h2>Configuration</h2> * <table><tbody> * <tr><th>layouts</th> * <td>List of layouts.</td> * <td>req</td> * <td>Configuration</td> * <td><code>null</code></td> * </tr> * <tr> * <th>layouts/layout</th> * <td>Multiple configured layouts. * </td> * <td>req</td> * <td>Configuration</td> * <td><code>null</code></td> * </tr> * <tr> * <th>layouts/layout/attribute::name</th> * <td>Unique layout name.</td> * <td>req</td> * <td>String</td> * <td><code>null</code></td> * </tr> * <tr> * <th>layouts/layout/attribute::create-id</th> * <td></td> * <td></td> * <td>boolean</td> * <td><code>false</code></td> * </tr> * <tr> * <th>layouts/layout/renderers/attribute::default</th> * <td></td> * <td>req</td> * <td>String</td> * <td><code>null</code></td> * </tr> * <tr> * <th>layouts/layout/renderers/renderer</th> * <td></td> * <td>req</td> * <td>Configuration</td> * <td><code>null</code></td> * </tr> * <tr> * <th>layouts/layout/renderers/renderer/attribute::name</th> * <td></td> * <td>req</td> * <td>String</td> * <td><code>null</code></td> * </tr> * <tr> * <th>layouts/layout/aspects/aspect</th> * <td></td> * <td>req</td> * <td>String</td> * <td><code>null</code></td> * </tr> * </tbody></table> * * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a> * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a> * * @version CVS $Id$ */ public class DefaultLayoutFactory extends AbstractLogEnabled implements ThreadSafe, Component, LayoutFactory, Configurable, Disposable, Serviceable, Initializable, Receiver { protected Map layouts = new HashMap(); protected List descriptions = new ArrayList(); protected ServiceSelector storeSelector; protected ServiceManager manager; protected Configuration[] layoutsConf; protected static long idCounter = System.currentTimeMillis(); /* (non-Javadoc) * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) */ public void service(ServiceManager manager) throws ServiceException { this.manager = manager; this.storeSelector = (ServiceSelector)this.manager.lookup( AspectDataStore.ROLE+"Selector" ); } /** * Configure a layout */ protected void configureLayout(Configuration layoutConf) throws ConfigurationException { DefaultLayoutDescription desc = new DefaultLayoutDescription(); final String name = layoutConf.getAttribute("name"); // unique test if ( this.layouts.get(name) != null) { throw new ConfigurationException("Layout name must be unique. Double definition for " + name); } desc.setName(name); desc.setClassName(layoutConf.getAttribute("class")); desc.setCreateId(layoutConf.getAttributeAsBoolean("create-id", false)); desc.setItemClassName(layoutConf.getAttribute("item-class", null)); // the renderers final String defaultRenderer = layoutConf.getChild("renderers").getAttribute("default"); desc.setDefaultRendererName(defaultRenderer); final Configuration[] rendererConfs = layoutConf.getChild("renderers").getChildren("renderer"); if ( rendererConfs != null ) { boolean found = false; for(int m=0; m < rendererConfs.length; m++) { final String rName = rendererConfs[m].getAttribute("name"); desc.addRendererName(rName); if ( defaultRenderer.equals(rName) ) { found = true; } } if ( !found ) { throw new ConfigurationException("Default renderer '" + defaultRenderer + "' is not configured for layout '" + name + "'"); } } else { throw new ConfigurationException("Default renderer '" + defaultRenderer + "' is not configured for layout '" + name + "'"); } // and now the aspects final Configuration[] aspectsConf = layoutConf.getChild("aspects").getChildren("aspect"); if (aspectsConf != null) { for(int m=0; m < aspectsConf.length; m++) { AspectDescription adesc = DefaultAspectDescription.newInstance(aspectsConf[m]); desc.addAspectDescription( adesc ); } } // now query all configured renderers for their aspects PortalService service = null; try { service = (PortalService)this.manager.lookup(PortalService.ROLE); PortalComponentManager pcManager = service.getComponentManager(); Iterator rendererIterator = desc.getRendererNames(); while (rendererIterator.hasNext()) { final String rendererName = (String)rendererIterator.next(); Renderer renderer = pcManager.getRenderer( rendererName ); Iterator aspectIterator = renderer.getAspectDescriptions(); while (aspectIterator.hasNext()) { final AspectDescription adesc = (AspectDescription) aspectIterator.next(); desc.addAspectDescription( adesc ); } } } catch (ServiceException ce ) { throw new ConfigurationException("Unable to lookup renderer selector.", ce); } finally { this.manager.release( service ); } // set the aspect data handler DefaultAspectDataHandler handler = new DefaultAspectDataHandler(desc, this.storeSelector); this.layouts.put(desc.getName(), new Object[] {desc, handler}); this.descriptions.add(desc); } /* (non-Javadoc) * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) */ public void configure(Configuration configuration) throws ConfigurationException { this.layoutsConf = configuration.getChild("layouts").getChildren("layout"); } protected void init() { // FIXME when we switch to another container we can remove // the lazy evaluation if ( this.layoutsConf != null ) { synchronized (this) { if ( this.layoutsConf != null ) { for(int i=0; i < layoutsConf.length; i++ ) { try { this.configureLayout( layoutsConf[i] ); } catch (ConfigurationException ce) { throw new CascadingRuntimeException("Unable to configure layout.", ce); } } this.layoutsConf = null; } } } } /* (non-Javadoc) * @see org.apache.cocoon.portal.layout.LayoutFactory#prepareLayout(org.apache.cocoon.portal.layout.Layout) */ public void prepareLayout(Layout layout) throws ProcessingException { if ( layout != null ) { this.init(); final String layoutName = layout.getName(); if ( layoutName == null ) { throw new ProcessingException("Layout '"+layout.getId()+"' has no associated name."); } Object[] o = (Object[]) this.layouts.get( layoutName ); if ( o == null ) { throw new ProcessingException("LayoutDescription with name '" + layoutName + "' not found."); } DefaultLayoutDescription layoutDescription = (DefaultLayoutDescription)o[0]; layout.setDescription( layoutDescription ); layout.setAspectDataHandler((AspectDataHandler)o[1]); // recursive if ( layout instanceof CompositeLayout ) { CompositeLayout composite = (CompositeLayout)layout; composite.setItemClassName(layoutDescription.getItemClassName()); Iterator items = composite.getItems().iterator(); while ( items.hasNext() ) { this.prepareLayout( ((Item)items.next()).getLayout() ); } } } } /* (non-Javadoc) * @see org.apache.cocoon.portal.layout.LayoutFactory#newInstance(java.lang.String) */ public Layout newInstance(String layoutName) throws ProcessingException { this.init(); Object[] o = (Object[]) this.layouts.get( layoutName ); if ( o == null ) { throw new ProcessingException("LayoutDescription with name '" + layoutName + "' not found."); } DefaultLayoutDescription layoutDescription = (DefaultLayoutDescription)o[0]; Layout layout = null; try { Class clazz = ClassUtils.loadClass( layoutDescription.getClassName() ); layout = (Layout)clazz.newInstance(); } catch (Exception e) { throw new ProcessingException("Unable to create new instance", e ); } String id = null; if ( layoutDescription.createId() ) { synchronized (this) { id = layoutName + '_' + idCounter; idCounter += 1; } } layout.initialize( layoutName, id ); layout.setDescription( layoutDescription ); layout.setAspectDataHandler((AspectDataHandler)o[1]); if ( layout instanceof CompositeLayout ) { CompositeLayout composite = (CompositeLayout)layout; composite.setItemClassName(layoutDescription.getItemClassName()); } PortalService service = null; try { service = (PortalService)this.manager.lookup(PortalService.ROLE); service.getComponentManager().getProfileManager().register(layout); } catch (ServiceException ce) { throw new ProcessingException("Unable to lookup profile manager.", ce); } finally { this.manager.release( service ); } return layout; } /* (non-Javadoc) * @see org.apache.cocoon.portal.layout.LayoutFactory#getLayoutDescriptions() */ public List getLayoutDescriptions() { this.init(); return this.descriptions; } /* (non-Javadoc) * @see org.apache.avalon.framework.activity.Disposable#dispose() */ public void dispose() { if ( this.manager != null ) { EventManager eventManager = null; try { eventManager = (EventManager)this.manager.lookup(EventManager.ROLE); eventManager.unsubscribe( this ); } catch (Exception ignore) { // ignore } finally { this.manager.release( eventManager ); } this.manager.release( this.storeSelector ); this.storeSelector = null; this.manager = null; } } /* (non-Javadoc) * @see org.apache.avalon.framework.activity.Initializable#initialize() */ public void initialize() throws Exception { EventManager eventManager = null; try { eventManager = (EventManager)this.manager.lookup(EventManager.ROLE); eventManager.subscribe( this ); } finally { this.manager.release( eventManager ); } } /** * @see Receiver */ public void inform(LayoutEvent event, PortalService service) { Layout layout = (Layout)event.getTarget(); if ( event instanceof LayoutRemoveEvent ) { try { this.remove( layout ); } catch (ProcessingException pe) { throw new CascadingRuntimeException("Exception during removal.", pe); } } } /* (non-Javadoc) * @see org.apache.cocoon.portal.layout.LayoutFactory#remove(org.apache.cocoon.portal.layout.Layout) */ public void remove(Layout layout) throws ProcessingException { if ( layout != null ) { this.init(); if ( layout instanceof CompositeLayout ) { final CompositeLayout cl = (CompositeLayout)layout; while ( cl.getItems().size() > 0 ) { final Item i = cl.getItem(0); this.remove( i.getLayout() ); } } Item parent = layout.getParent(); if ( parent != null && parent.getParent() != null) { parent.getParent().removeItem( parent ); } PortalService service = null; EventManager eventManager = null; try { service = (PortalService)this.manager.lookup(PortalService.ROLE); ProfileManager profileManager = service.getComponentManager().getProfileManager(); if ( layout instanceof CopletLayout ) { // full screen? if ( layout.equals(service.getEntryLayout(null)) ) { Event event = new FullScreenCopletEvent(((CopletLayout)layout).getCopletInstanceData(), null); eventManager = (EventManager)this.manager.lookup(EventManager.ROLE); eventManager.send(event); service.getComponentManager().getLinkService().addEventToLink(event); } CopletFactory factory = service.getComponentManager().getCopletFactory(); factory.remove( ((CopletLayout)layout).getCopletInstanceData()); } profileManager.unregister(layout); } catch (ServiceException ce) { throw new ProcessingException("Unable to lookup portal service.", ce); } finally { this.manager.release( service ); this.manager.release(eventManager); } } } }