Package org.apache.cocoon.webapps.portal.components

Source Code of org.apache.cocoon.webapps.portal.components.PortalManagerImpl

/*
* 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.webapps.portal.components;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.avalon.excalibur.pool.Recyclable;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.Recomposable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.components.sax.XMLDeserializer;
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.environment.CocoonRunnable;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.environment.Response;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.webapps.authentication.AuthenticationManager;
import org.apache.cocoon.webapps.authentication.user.RequestState;
import org.apache.cocoon.webapps.portal.PortalConstants;
import org.apache.cocoon.webapps.session.ContextManager;
import org.apache.cocoon.webapps.session.MediaManager;
import org.apache.cocoon.webapps.session.SessionManager;
import org.apache.cocoon.webapps.session.TransactionManager;
import org.apache.cocoon.webapps.session.context.SessionContext;
import org.apache.cocoon.webapps.session.xml.XMLUtil;
import org.apache.cocoon.xml.IncludeXMLConsumer;
import org.apache.cocoon.xml.XMLConsumer;
import org.apache.cocoon.xml.XMLUtils;
import org.apache.cocoon.xml.dom.DOMUtil;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceParameters;
import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.store.Store;
import org.apache.excalibur.xml.xpath.XPathProcessor;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

/**
*  This is the basis portal component
*
* @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
* @version CVS $Id: PortalManagerImpl.java 649886 2008-04-20 05:02:27Z joerg $
*/
public final class PortalManagerImpl
extends AbstractLogEnabled
implements Disposable, Composable, Recomposable, Recyclable, Contextualizable, Component, PortalManager {

    /** The cache (store) for the profiles */
    private Store   profileStore;

    /** The authenticationManager */
    private AuthenticationManager authenticationManager;

    /** The media manager */
    private MediaManager mediaManager;

    /** The XPath Processor */
    private XPathProcessor xpathProcessor;

    /** The session manager */
    private SessionManager     sessionManager;

    /** The Context manager */
    private ContextManager     contextManager;

    /** The transaction manager */
    private TransactionManager transactionManager;

    /** The component manager */
    protected ComponentManager manager;

    /** The current source resolver */
    protected SourceResolver resolver;

    /** The context */
    protected Context componentContext;

    /** Are we already setup for this request? */
    protected boolean initialized = false;

    /* (non-Javadoc)
     * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
     */
    public void recycle() {
        if (this.manager != null) {
            this.manager.release(this.profileStore);
            this.manager.release( (Component)this.authenticationManager);
            this.manager.release( (Component)this.mediaManager);
            this.manager.release( (Component)this.sessionManager);
            this.manager.release( (Component)this.contextManager);
            this.manager.release( (Component)this.transactionManager);
            this.profileStore = null;
            this.authenticationManager = null;
            this.mediaManager = null;
            this.transactionManager = null;
            this.sessionManager = null;
            this.contextManager = null;
        }
        this.initialized = false;
    }

    /**
     * Get the current authentication state
     */
    protected RequestState getRequestState() {
        AuthenticationManager authManager = null;
        try {
            authManager = (AuthenticationManager)this.manager.lookup(AuthenticationManager.ROLE);
            return authManager.getState();
        } catch (ComponentException ce) {
            // ignore this here
            return null;
        } finally {
            this.manager.release( (Component)authManager );
        }
    }

    /* (non-Javadoc)
     * @see org.apache.avalon.framework.component.Composable#compose(org.apache.avalon.framework.component.ComponentManager)
     */
    public void compose(ComponentManager manager)
    throws ComponentException {
        this.manager = manager;
        this.resolver = (SourceResolver)manager.lookup(SourceResolver.ROLE);
        this.xpathProcessor = (XPathProcessor)this.manager.lookup(XPathProcessor.ROLE);
    }

    /* (non-Javadoc)
     * @see org.apache.avalon.framework.component.Recomposable#recompose(org.apache.avalon.framework.component.ComponentManager)
     */
    public void recompose(ComponentManager manager) throws ComponentException {
        this.manager = manager;
    }

    /* (non-Javadoc)
     * @see org.apache.avalon.framework.activity.Disposable#dispose()
     */
    public void dispose() {
        if ( this.manager != null ) {
            this.manager.release( (Component)this.xpathProcessor );
            this.xpathProcessor = null;
            this.manager.release( (Component)this.resolver );
            this.resolver = null;
        }
    }

    /* (non-Javadoc)
     * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
     */
    public void contextualize(Context context) throws ContextException {
        this.componentContext = context;
    }

    /**
     * Get the profile store
     */
    protected Store getProfileStore()
    throws ProcessingException {
        if (this.profileStore == null) {
            try {
                this.profileStore = (Store)this.manager.lookup(Store.ROLE);
            } catch (ComponentException ce) {
                throw new ProcessingException("Error during lookup of store component.", ce);
            }
        }
        return this.profileStore;
    }

    /**
     * Get the authentication manager
     */
    protected AuthenticationManager getAuthenticationManager()
    throws ProcessingException {
        if (this.authenticationManager == null) {
            try {
                this.authenticationManager = (AuthenticationManager)this.manager.lookup(AuthenticationManager.ROLE);
            } catch (ComponentException ce) {
                throw new ProcessingException("Error during lookup of AuthenticationManager.", ce);
            }
        }
        return this.authenticationManager;
    }

    /**
     * Get the media manager
     */
    protected MediaManager getMediaManager()
    throws ProcessingException {
        if (this.mediaManager == null) {
            try {
                this.mediaManager = (MediaManager)this.manager.lookup(MediaManager.ROLE);
            } catch (ComponentException ce) {
                throw new ProcessingException("Error during lookup of MediaManager.", ce);
            }
        }
        return this.mediaManager;
    }

    /**
     * Setup this component
     */
    protected void setup()
    throws ProcessingException {
        if ( !this.initialized ) {

            final Request request = ContextHelper.getRequest(this.componentContext);

            if ( request.getAttribute(PortalManager.ROLE) == null ) {

                request.setAttribute(PortalManager.ROLE, Boolean.TRUE);

                // Get and ignore the configuration
                this.getConfiguration();

                try {
                    this.changeProfile();
                } catch (SAXException se) {
                    throw new ProcessingException(se);
                } catch (IOException ioe) {
                    throw new ProcessingException(ioe);
                }
            }

            this.initialized = true;
        }
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#configurationTest()
     */
    public void configurationTest()
    throws ProcessingException, IOException, SAXException {
        // no sync required
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN configurationTest");
        }

        this.setup();

        // Ignore result
        this.getConfiguration();

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END configurationTest");
        }
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#getContext(boolean)
     */
    public SessionContext getContext(boolean create)
    throws ProcessingException, IOException, SAXException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN getContext create="+create);
        }
        this.setup();
        SessionContext context = null;

        final Session session = this.getSessionManager().getSession(false);
        if (session != null) {
            synchronized(session) {
                String appName = this.getRequestState().getApplicationName();
                String attrName = PortalConstants.PRIVATE_SESSION_CONTEXT_NAME;
                if (appName != null) {
                    attrName = attrName + ':' + appName;
                }
                context = this.getContextManager().getContext(attrName);
                if (context == null && create) {

                    // create new context

                    context = this.getAuthenticationManager().createApplicationContext(attrName, null, null);

                }
            } // end synchronized
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END getContext context="+context);
        }
        return context;
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#streamConfiguration(org.apache.cocoon.xml.XMLConsumer, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
     */
    public void streamConfiguration(XMLConsumer consumer,
                                    String      requestURI,
                                    String      profileID,
                                    String      media,
                                    String      contextID)
    throws IOException, SAXException, ProcessingException {
        // synchronized not req.
        this.setup();
        Response response = ContextHelper.getResponse(this.componentContext);

        XMLUtils.startElement(consumer, PortalConstants.ELEMENT_CONFIGURATION);

        // set the portal-page uri:
        StringBuffer buffer = new StringBuffer(requestURI);
        buffer.append((requestURI.indexOf('?') == -1 ? '?' : '&'))
            .append(PortalManagerImpl.REQ_PARAMETER_PROFILE)
            .append('=')
            .append(profileID);
        String uri = response.encodeURL(buffer.toString());
        XMLUtils.startElement(consumer, "uri");
        XMLUtils.data(consumer, uri);
        XMLUtils.endElement(consumer, "uri");

        Map config = this.getConfiguration();
        String portalURI = response.encodeURL((String)config.get(PortalConstants.CONF_PORTAL_URI));

        XMLUtils.startElement(consumer, "portal");
        XMLUtils.data(consumer, portalURI);
        XMLUtils.endElement(consumer, "portal");

        XMLUtils.startElement(consumer, PortalConstants.ELEMENT_PROFILE);
        XMLUtils.data(consumer, profileID);
        XMLUtils.endElement(consumer, PortalConstants.ELEMENT_PROFILE);

        if (media != null) {
            XMLUtils.startElement(consumer, "media");
            XMLUtils.data(consumer, media);
            XMLUtils.endElement(consumer, "media");
        }

        if (contextID != null) {
            XMLUtils.startElement(consumer, "context");
            XMLUtils.data(consumer, contextID);
            XMLUtils.endElement(consumer, "context");
        }

        XMLUtils.endElement(consumer, PortalConstants.ELEMENT_CONFIGURATION);
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#showAdminConf(org.apache.cocoon.xml.XMLConsumer)
     */
    public void showAdminConf(XMLConsumer consumer)
    throws SAXException, ProcessingException, IOException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN showAdminConf consumer=" + consumer);
        }
        this.setup();
        Request request = ContextHelper.getRequest(this.componentContext);
        try {
            String profileID = "global";
            String copletID = request.getParameter(PortalManagerImpl.REQ_PARAMETER_COPLET);

            SessionContext context = this.getContext(true);

            Map configuration = this.getConfiguration();

            DocumentFragment copletsFragment = (DocumentFragment)context.getAttribute(ATTRIBUTE_ADMIN_COPLETS);
            String command = request.getParameter(PortalManagerImpl.REQ_PARAMETER_ADMIN_COPLETS);
            if (command != null && copletsFragment != null) {
                try {
                    this.getTransactionManager().startWritingTransaction(context);
                    // save : save coplets base
                    // new  : new coplet
                    // delete : use id to delete coplet
                    // change : change the coplet
                    //        cache : cleans the cache
                    if (command.equals("delete") && copletID != null) {
                        Node coplet = DOMUtil.getSingleNode(copletsFragment, "coplets-profile/coplets/coplet[@id='"+copletID+"']", this.xpathProcessor);
                        if (coplet != null) {
                            coplet.getParentNode().removeChild(coplet);
                        }
                    } else if (command.equals("change") && copletID != null) {
                        Node coplet = DOMUtil.getSingleNode(copletsFragment, "coplets-profile/coplets/coplet[@id='"+copletID+"']", this.xpathProcessor);
                        if (coplet != null) {
                            // now get the information
                            String value;

                            value = request.getParameter("portaladmin_title");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.getSingleNode(coplet, "title", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_mand");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.getSingleNode(coplet, "configuration/mandatory", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_sizable");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.getSingleNode(coplet, "configuration/sizable", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_active");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.getSingleNode(coplet, "configuration/active", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_handsize");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/handlesSizable", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_handpar");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/handlesParameters", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_timeout");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/timeout", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_customizable");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/customizable", this.xpathProcessor), value);

                            value = request.getParameter("portaladmin_persistent");
                            if (value != null) DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/persistent", this.xpathProcessor), value);

                            String resource = request.getParameter("portaladmin_resource");
                            if (resource != null) {
                                Element resourceNode = (Element)DOMUtil.getSingleNode(coplet, "resource", this.xpathProcessor);
                                resourceNode.getParentNode().removeChild(resourceNode);
                                resourceNode = coplet.getOwnerDocument().createElementNS(null, "resource");
                                resourceNode.setAttributeNS(null, "uri", resource);
                                coplet.appendChild(resourceNode);
                            }
                            resource = request.getParameter("portaladmin_cust");
                            boolean isCustom = DOMUtil.getValueAsBooleanOf(coplet, "configuration/customizable", false, this.xpathProcessor);
                            if (resource != null && isCustom ) {
                                Element resourceNode = (Element)DOMUtil.getSingleNode(coplet, "customization", this.xpathProcessor);
                                if (resourceNode != null) resourceNode.getParentNode().removeChild(resourceNode);
                                resourceNode = coplet.getOwnerDocument().createElementNS(null, "customization");
                                resourceNode.setAttributeNS(null, "uri", resource);
                                coplet.appendChild(resourceNode);
                            }
                            if (!isCustom) {
                                Element resourceNode = (Element)DOMUtil.getSingleNode(coplet, "customization", this.xpathProcessor);
                                if (resourceNode != null) resourceNode.getParentNode().removeChild(resourceNode);
                            }

                            // transformations
                            value = request.getParameter("portaladmin_newxsl");
                            if (value != null) {
                                Element tNode = (Element)DOMUtil.selectSingleNode(coplet, "transformation", this.xpathProcessor);
                                Element sNode = tNode.getOwnerDocument().createElementNS(null, "stylesheet");
                                tNode.appendChild(sNode);
                                sNode.appendChild(sNode.getOwnerDocument().createTextNode(value));
                            }

                            // now get all transformation stylesheets, mark
                            // all stylesheets which should be deleted with
                            // an attribute delete
                            Enumeration keys = request.getParameterNames();
                            Element sNode;
                            String key;
                            while (keys.hasMoreElements() ) {
                                key = (String)keys.nextElement();
                                if (key.startsWith("portaladmin_xsl_") ) {
                                    value = key.substring(key.lastIndexOf('_')+ 1);
                                    sNode = (Element)DOMUtil.getSingleNode(coplet, "transformation/stylesheet[position()="+value+"]", this.xpathProcessor);
                                    if (sNode != null) {
                                        String xslName = request.getParameter(key);
                                        if (xslName.equals("true") ) xslName = "**STYLESHEET**";
                                        DOMUtil.setValueOfNode(sNode, xslName);
                                    }
                                } else if (key.startsWith("portaladmin_delxsl_") ) {
                                    value = key.substring(key.lastIndexOf('_')+ 1);
                                    sNode = (Element)DOMUtil.getSingleNode(coplet, "transformation/stylesheet[position()="+value+"]", this.xpathProcessor);
                                    if (sNode != null) {
                                        sNode.setAttributeNS(null, "delete", "true");
                                    }
                                }
                            }
                            NodeList delete = DOMUtil.selectNodeList(coplet, "transformation/stylesheet[@delete]", this.xpathProcessor);
                            if (delete != null) {
                                for(int i=0; i < delete.getLength(); i++) {
                                    delete.item(i).getParentNode().removeChild(delete.item(i));
                                }
                            }
                        }
                    } else if (command.equals("new") ) {
                        // first we have to invent a new coplet id!
                        int index = 0;
                        boolean found = false;
                        Element coplet;
                        Element subNode;

                        while (!found) {
                            copletID = "S"+index;
                            coplet = (Element)DOMUtil.getSingleNode(copletsFragment, "coplets-profile/coplets/coplet[@id='"+copletID+"']", this.xpathProcessor);
                            if (coplet == null) {
                                found = true;
                            } else {
                                index++;
                            }
                        }
                        coplet = copletsFragment.getOwnerDocument().createElementNS(null, "coplet");
                        coplet.setAttributeNS(null, "id", copletID);
                        subNode = coplet.getOwnerDocument().createElementNS(null, "resource");
                        coplet.appendChild(subNode);
                        subNode.setAttributeNS(null, "uri", "uri_in_sitemap");

                        String title = request.getParameter("portaladmin_title");
                        if (title == null || title.trim().length() == 0) title = "**NEW COPLET**";
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/mandatory", this.xpathProcessor), "false");
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/sizable", this.xpathProcessor), "true");
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/active", this.xpathProcessor), "false");
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/handlesParameters", this.xpathProcessor), "true");
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "configuration/handlesSizable", this.xpathProcessor), "false");
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "title", this.xpathProcessor), title);
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "status/visible", this.xpathProcessor), "true");
                        DOMUtil.setValueOfNode(DOMUtil.selectSingleNode(coplet, "status/size", this.xpathProcessor), "max");
                        DOMUtil.getSingleNode(copletsFragment, "coplets-profile/coplets", this.xpathProcessor).appendChild(coplet);
                    } else if (command.equals("save") ) {

                        SourceParameters pars = new SourceParameters();
                        pars.setSingleParameterValue("profile", "coplet-base");
                        RequestState state = this.getRequestState();
                        pars.setSingleParameterValue("application", state.getApplicationName());
                        pars.setSingleParameterValue("handler", state.getHandlerName());

                        String saveResource = (String)configuration.get(PortalConstants.CONF_COPLETBASE_SAVE_RESOURCE);

                        if (saveResource == null) {
                            throw new ProcessingException("portal: No save resource defined for type coplet-base.");
                        } else {

                            SourceUtil.writeDOM(saveResource,
                                                null,
                                                pars,
                                                copletsFragment,
                                                this.resolver,
                                                "xml");

                            // now the hardest part, clean up the whole cache
                            this.cleanUpCache(null, null, configuration);
                        }
                    }
                } finally {
                    this.getTransactionManager().stopWritingTransaction(context);
                }
            }

            // general commands
            if (command != null && command.equals("cleancache") ) {
                this.cleanUpCache(null, null, configuration);
            }

            String state = request.getParameter(PortalManagerImpl.REQ_PARAMETER_STATE);
            if (state == null) {
                state = (String)context.getAttribute(ATTRIBUTE_ADMIN_STATE, PortalConstants.STATE_MAIN);
            }

            // now start producing xml:
            consumer.startElement("", PortalConstants.ELEMENT_ADMINCONF, PortalConstants.ELEMENT_ADMINCONF, XMLUtils.EMPTY_ATTRIBUTES);

            context.setAttribute(ATTRIBUTE_ADMIN_STATE, state);
            consumer.startElement("", PortalConstants.ELEMENT_STATE, PortalConstants.ELEMENT_STATE, XMLUtils.EMPTY_ATTRIBUTES);
            consumer.characters(state.toCharArray(), 0, state.length());
            consumer.endElement("", PortalConstants.ELEMENT_STATE, PortalConstants.ELEMENT_STATE);

            if (state.equals(PortalConstants.STATE_MAIN) ) {

                Document rolesDF = this.getRoles();
                Node     roles   = null;
                if (rolesDF != null) roles = DOMUtil.getSingleNode(rolesDF, "roles", this.xpathProcessor);
                IncludeXMLConsumer.includeNode(roles, consumer, consumer);
            }

            if (state.equals(PortalConstants.STATE_MAIN_ROLE) ) {

                Document rolesDF = this.getRoles();
                Node     roles   = null;
                if (rolesDF != null) roles = DOMUtil.getSingleNode(rolesDF, "roles", this.xpathProcessor);
                IncludeXMLConsumer.includeNode(roles, consumer, consumer);

                String role = request.getParameter(PortalManagerImpl.REQ_PARAMETER_ROLE);
                if (role == null) {
                    role = (String)context.getAttribute(ATTRIBUTE_ADMIN_ROLE);
                }
                context.setAttribute(ATTRIBUTE_ADMIN_ROLE, role);
                if (role != null) {
                    XMLUtils.startElement(consumer, "roleusers");
                    XMLUtils.startElement(consumer, "name");
                    XMLUtils.data(consumer, role);
                    XMLUtils.endElement(consumer, "name");
                    Document userDF = this.getUsers(role, null);
                    Node     users = null;
                    if (userDF != null) users = DOMUtil.getSingleNode(userDF, "users", this.xpathProcessor);
                    IncludeXMLConsumer.includeNode(users, consumer, consumer);
                    XMLUtils.endElement(consumer, "roleusers");
                }
            }

            if (state.equals(PortalConstants.STATE_GLOBAL)) {
                profileID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, true);
                Map profile = this.retrieveProfile(profileID);
                if (profile == null) {
                    this.createProfile(context, PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, true);
                    profile = this.retrieveProfile(profileID);
                }
                this.showPortal(consumer, true, context, profile, profileID);
            }

            if (state.equals(PortalConstants.STATE_ROLE) ) {
                String role = request.getParameter(PortalManagerImpl.REQ_PARAMETER_ROLE);
                if (role == null) {
                    role = (String)context.getAttribute(ATTRIBUTE_ADMIN_ROLE);
                }
                context.setAttribute(ATTRIBUTE_ADMIN_ROLE, role);
                if (role != null) {
                    consumer.startElement("", PortalConstants.ELEMENT_ROLE, PortalConstants.ELEMENT_ROLE, XMLUtils.EMPTY_ATTRIBUTES);
                    consumer.characters(role.toCharArray(), 0, role.length());
                    consumer.endElement("", PortalConstants.ELEMENT_ROLE, PortalConstants.ELEMENT_ROLE);
                    profileID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ROLE, role, null, true);
                    Map profile = this.retrieveProfile(profileID);
                    if (profile == null) {
                        this.createProfile(context, PortalManagerImpl.BUILDTYPE_VALUE_ROLE, role, null, true);
                        profile = this.retrieveProfile(profileID);
                    }
                    this.showPortal(consumer, true, context, profile, profileID);
                }
            }
            if (state.equals(PortalConstants.STATE_USER) ) {
                String role = request.getParameter(PortalManagerImpl.REQ_PARAMETER_ROLE);
                String id   = request.getParameter(PortalManagerImpl.REQ_PARAMETER_ID);
                if (role == null) {
                    role = (String)context.getAttribute(ATTRIBUTE_ADMIN_ROLE);
                }
                if (id == null) {
                    id = (String)context.getAttribute(ATTRIBUTE_ADMIN_ID);
                }
                context.setAttribute(ATTRIBUTE_ADMIN_ID, id);
                context.setAttribute(ATTRIBUTE_ADMIN_ROLE, role);
                if (role != null && id != null) {
                    consumer.startElement("", PortalConstants.ELEMENT_ROLE, PortalConstants.ELEMENT_ROLE, XMLUtils.EMPTY_ATTRIBUTES);
                    consumer.characters(role.toCharArray(), 0, role.length());
                    consumer.endElement("", PortalConstants.ELEMENT_ROLE, PortalConstants.ELEMENT_ROLE);
                    consumer.startElement("", PortalConstants.ELEMENT_ID, PortalConstants.ELEMENT_ID, XMLUtils.EMPTY_ATTRIBUTES);
                    consumer.characters(id.toCharArray(), 0, id.length());
                    consumer.endElement("", PortalConstants.ELEMENT_ID, PortalConstants.ELEMENT_ID);

                    profileID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ID, role, id, true);
                    Map profile = this.retrieveProfile(profileID);
                    if (profile == null) {
                        this.createProfile(context, PortalManagerImpl.BUILDTYPE_VALUE_ID, role, id, true);
                        profile = this.retrieveProfile(profileID);
                    }
                    this.showPortal(consumer, true, context, profile, profileID);
                }
            }
            // one coplet
            if (state.equals(PortalConstants.STATE_COPLET) ) {
                if (copletsFragment != null && copletID != null) {
                    Node coplet = DOMUtil.getSingleNode(copletsFragment, "coplets-profile/coplets/coplet[@id='"+copletID+"']", this.xpathProcessor);
                    if (coplet != null) {
                        IncludeXMLConsumer.includeNode(coplet, consumer, consumer);
                    }
                } else {
                    state = PortalConstants.STATE_COPLETS;
                }
            }
            if (state.equals(PortalConstants.STATE_COPLETS) ) {
                consumer.startElement("", PortalConstants.ELEMENT_COPLETS, PortalConstants.ELEMENT_COPLETS, XMLUtils.EMPTY_ATTRIBUTES);

                // load the base coplets profile
                if (copletsFragment == null) {
                    SourceParameters pars = new SourceParameters();
                    RequestState reqstate = this.getRequestState();
                    pars.setSingleParameterValue("application", reqstate.getApplicationName());
                    String res = (String)configuration.get(PortalConstants.CONF_COPLETBASE_RESOURCE);
                    if (res == null) {
                        throw new ProcessingException("No configuration for portal-coplet base profile found.");
                    }
                    copletsFragment = SourceUtil.readDOM(res,
                                                         null,
                                                         pars,
                                                         this.resolver);
                    context.setAttribute(ATTRIBUTE_ADMIN_COPLETS, copletsFragment);
                }
                IncludeXMLConsumer.includeNode(DOMUtil.selectSingleNode(copletsFragment,
                                   "coplets-profile", this.xpathProcessor), consumer, consumer);
                consumer.endElement("", PortalConstants.ELEMENT_COPLETS, PortalConstants.ELEMENT_COPLETS);
            }

            // configuration
            this.streamConfiguration(consumer, request.getRequestURI(), profileID, null, null);

            consumer.endElement("", PortalConstants.ELEMENT_ADMINCONF, PortalConstants.ELEMENT_ADMINCONF);
        } catch (javax.xml.transform.TransformerException local) {
            throw new ProcessingException("TransformerException: " + local, local);
        }

        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("END showAdminConf");
        }
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#getStatusProfile()
     */
    public Element getStatusProfile()
    throws SAXException, IOException, ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("BEGIN getStatusProfile");
        }
        this.setup();
        SessionContext context = this.getContext(true);
        String profileID = null;
        Map storedProfile = null;
        Element statusProfile = null;

        if (context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE) != null) {
            profileID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ID,
                  (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE),
                  (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ID), false);
            storedProfile = this.retrieveProfile(profileID);
        }

        if (storedProfile != null) {
            DocumentFragment profile = (DocumentFragment)storedProfile.get(PortalConstants.PROFILE_PROFILE);
            try {
                statusProfile = (Element)DOMUtil.getSingleNode(profile, "profile/status-profile", this.xpathProcessor);
            } catch (javax.xml.transform.TransformerException ignore) {
            }
        }

        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("END getStatusProfile statusProfile="+(statusProfile == null ? "null" : XMLUtils.serializeNode(statusProfile, XMLUtils.createPropertiesForXML(false))));
        }
        return statusProfile;
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#showPortal(org.apache.cocoon.xml.XMLConsumer, boolean, boolean)
     */
    public void showPortal(XMLConsumer consumer,
                           boolean configMode,
                           boolean adminProfile)
    throws SAXException, ProcessingException, IOException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN showPortal consumer=" + consumer+", configMode="+
                             configMode+", adminProfile="+adminProfile);
        }
        this.setup();

        SessionContext context = this.getContext(true);
        String profileID = null;
        Map storedProfile = null;

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("start portal generation");
        }
        if (context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE) != null) {
            profileID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ID,
                  (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE),
                  (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ID), adminProfile);
            storedProfile = this.retrieveProfile(profileID);
        }
        if (storedProfile == null) {

            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("start building profile");
            }
            this.createProfile(context, PortalManagerImpl.BUILDTYPE_VALUE_ID, null, null, adminProfile);
            // get the profileID
            profileID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ID,
                    (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE),
                    (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ID), adminProfile);
            storedProfile = this.retrieveProfile(profileID);
            if (storedProfile == null) {
                throw new ProcessingException("portal: No portal profile found.");
            }
            if (this.getLogger().isDebugEnabled() ) {
                this.getLogger().debug("end building profile");
            }
        }

        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("start showing profile");
        }
        this.showPortal(consumer,
                        configMode,
                        context,
                        storedProfile,
                        profileID);
        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("end showing profile");
            this.getLogger().debug("end portal generation");
        }
        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("END showPortal");
        }
    }

    /**
     * Stream all layout information for the current portal
     * to the consumer.
     * The resulting XML:
     * <layout>
     *     <portal>
     *         ...
     *     </portal>
     *     <coplets>
     *         ...
     *     </coplets>
     * </layout>
     */
    public static void streamLayoutProfile(XMLConsumer consumer,
                                           Map         portalLayouts,
                                           Map         copletLayouts,
                                           String      mediaType)
    throws SAXException {
        Element  element;
        NodeList childs;
        Attributes attr = new AttributesImpl();

        consumer.startElement("", PortalConstants.ELEMENT_LAYOUT, PortalConstants.ELEMENT_LAYOUT, attr);

        // first: layout of portal
        consumer.startElement("", PortalConstants.ELEMENT_PORTAL, PortalConstants.ELEMENT_PORTAL, attr);

        element = (Element)portalLayouts.get(mediaType);
        childs = element.getChildNodes();
        for(int ci = 0; ci < childs.getLength(); ci++) {
            IncludeXMLConsumer.includeNode(childs.item(ci),
                                      consumer,
                                      consumer);
        }
        consumer.endElement("", PortalConstants.ELEMENT_PORTAL, PortalConstants.ELEMENT_PORTAL);

        // second: layout of coplets
        consumer.startElement("", PortalConstants.ELEMENT_COPLETS, PortalConstants.ELEMENT_COPLETS, attr);
        element = (Element)copletLayouts.get(mediaType);
        childs = element.getChildNodes();
        for(int ci = 0; ci < childs.getLength(); ci++) {
            IncludeXMLConsumer.includeNode(childs.item(ci),
                                      consumer,
                                      consumer);
        }
        consumer.endElement("", PortalConstants.ELEMENT_COPLETS, PortalConstants.ELEMENT_COPLETS);

        consumer.endElement("", PortalConstants.ELEMENT_LAYOUT, PortalConstants.ELEMENT_LAYOUT);
    }

    /**
     * Show the portal.
     * The portal is included in the current stream.
     */
    private void showPortal(XMLConsumer consumer,
                           boolean      configMode,
                           SessionContext context,
                           Map          storedProfile,
                           String       profileID)
    throws SAXException, ProcessingException, IOException {
        // synchronized
        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("BEGIN showPortal consumer=" + consumer+", configMode="+configMode+", context="+context+
                ", profile="+storedProfile);
        }
        Request request = ContextHelper.getRequest(this.componentContext);
        try {
            this.getTransactionManager().startReadingTransaction(context);

            DocumentFragment profile;
            Map              defaultCoplets;
            Map              mediaCoplets;
            Map              portalLayouts;
            Map              copleyLayouts;
            Node[]           miscNodes;
            String           mediaType = this.getMediaManager().getMediaType();

            profile = (DocumentFragment)storedProfile.get(PortalConstants.PROFILE_PROFILE);
            portalLayouts = (Map)storedProfile.get(PortalConstants.PROFILE_PORTAL_LAYOUTS);
            copleyLayouts = (Map)storedProfile.get(PortalConstants.PROFILE_COPLET_LAYOUTS);
            miscNodes = (Node[])storedProfile.get(PortalConstants.PROFILE_MISC_POINTER);
            defaultCoplets = (Map)storedProfile.get(PortalConstants.PROFILE_DEFAULT_COPLETS);
            mediaCoplets = (Map)storedProfile.get(PortalConstants.PROFILE_MEDIA_COPLETS);
            if (profile == null ||
                defaultCoplets == null ||
                mediaCoplets == null ||
                portalLayouts == null ||
                copleyLayouts == null ||
                miscNodes == null) {
                throw new ProcessingException("portal: No portal profile found.");
            }

            // get the configuration
            Map config = this.getConfiguration();
            if (config == null) {
                throw new ProcessingException("No configuration for portal found.");
            }
            boolean processCopletsParallel = false;
            long    defaultCopletTimeout   = 600000;

            Boolean boolValue = (Boolean)config.get(PortalConstants.CONF_PARALLEL_COPLETS);
            if (boolValue != null) processCopletsParallel = boolValue.booleanValue();
            Long longValue = (Long)config.get(PortalConstants.CONF_COPLET_TIMEOUT);
            if (longValue != null) defaultCopletTimeout = longValue.longValue();

            Element element;

            // now start producing xml:
            AttributesImpl attr = new AttributesImpl();
            if (configMode) {
                XMLUtils.startElement(consumer, PortalConstants.ELEMENT_PORTALCONF);
            } else {
                XMLUtils.startElement(consumer, PortalConstants.ELEMENT_PORTAL);
            }

            // configuration
            this.streamConfiguration(consumer, request.getRequestURI(), profileID, mediaType, null);

            // LAYOUT:
            if (configMode) {
                IncludeXMLConsumer.includeNode(DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","layout-profile"}, false),
                     consumer, consumer);
                // copletsConfiguration (only for configMode)
                IncludeXMLConsumer.includeNode(DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","coplets-profile"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","type-profile","typedefs"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","portal-profile"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","personal-profile"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","status-profile"}, false),
                     consumer, consumer);
            } else {
                PortalManagerImpl.streamLayoutProfile(consumer, portalLayouts, copleyLayouts, mediaType);
            }
            // END LAYOUT

            if (!configMode) {
                Element statusProfile = (Element)DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","status-profile"}, false);

                String copletNotAvailableMessage = "The coplet is currently not available.";
                Node messages = miscNodes[PortalConstants.PROFILE_MISC_MESSAGES_NODE];
                if (messages != null) {
                    messages = DOMUtil.getFirstNodeFromPath(messages, new String[] {"coplet_not_available"}, false);
                    if (messages != null) copletNotAvailableMessage = DOMUtil.getValueOfNode(messages,
                         copletNotAvailableMessage);
                }

                // LOAD COPLETS
                List[] copletContents;

                List[] temp = (List[])context.getAttribute(PortalConstants.ATTRIBUTE_COPLET_REPOSITORY);
                if (temp != null) {
                    copletContents = new List[temp.length];
                    for (int i = 0; i < temp.length; i++) {
                        if (temp[i] == null) {
                            copletContents[i] = null;
                        } else {
                            copletContents[i] = new ArrayList(temp[i]);
                        }
                    }
                } else {
                    copletContents = new List[PortalConstants.MAX_COLUMNS+2];
                    context.setAttribute(PortalConstants.ATTRIBUTE_COPLET_REPOSITORY, copletContents);
                }

                if (copletContents[0] == null) {
                    copletContents[0] = new ArrayList(1);
                } else {
                    copletContents[0].clear();
                }
                if (copletContents[1] == null) {
                    copletContents[1] = new ArrayList(1);
                } else {
                    copletContents[1].clear();
                }

                // test for header
                String value;
                value = DOMUtil.getValueOfNode(miscNodes[PortalConstants.PROFILE_MISC_HEADER_NODE]);
                if (value != null && new Boolean(value).booleanValue()) {
                    element = (Element)miscNodes[PortalConstants.PROFILE_MISC_HEADER_CONTENT_NODE];
                    if (element != null) {
                        this.loadCoplets(element,
                                         defaultCoplets,
                                         mediaCoplets,
                                         copletContents[0],
                                         processCopletsParallel,
                                         defaultCopletTimeout,
                                         statusProfile);
                    }
                }

                // content
                value = DOMUtil.getValueOfNode(miscNodes[PortalConstants.PROFILE_MISC_COLUMNS_NODE]);

                // for a simpler XSL-Stylesheet: The columns must be inserted in the
                // correct order!!!
                if (value != null && new Integer(value).intValue() > 0) {

                    Element columnElement;
                    int columns = new Integer(value).intValue();
                    if (columns > PortalConstants.MAX_COLUMNS) {
                        throw new ProcessingException("portal: Maximum number of columns supported is: "+PortalConstants.MAX_COLUMNS);
                    }

                    for(int colindex = 1; colindex <= columns; colindex++) {
                        if (copletContents[colindex+1] == null) {
                            copletContents[colindex+1] = new ArrayList(10);
                        } else {
                            copletContents[colindex+1].clear();
                        }
                        columnElement = (Element)miscNodes[7 + colindex];
                        element = (Element)DOMUtil.getFirstNodeFromPath(columnElement, new String[] {"coplets"}, false);
                        if (element != null) {
                            this.loadCoplets(element,
                                             defaultCoplets,
                                             mediaCoplets,
                                             copletContents[colindex+1],
                                             processCopletsParallel,
                                             defaultCopletTimeout,
                                             statusProfile);
                        }

                    }
                    for(int colindex = columns+2; colindex <= PortalConstants.MAX_COLUMNS+1; colindex++) {
                        if (copletContents[colindex] != null) {
                            copletContents[colindex] = null;
                        }
                    }

                } else {
                    for(int colindex = 1; colindex <= PortalConstants.MAX_COLUMNS; colindex++) {
                        if (copletContents[colindex+1] != null) {
                            copletContents[colindex+1] = null;
                        }
                    }
                }

                // test for footer
                value = DOMUtil.getValueOfNode(miscNodes[PortalConstants.PROFILE_MISC_FOOTER_NODE]);
                if (value != null && new Boolean(value).booleanValue()) {
                    element = (Element)miscNodes[PortalConstants.PROFILE_MISC_FOOTER_CONTENT_NODE];
                    if (element != null) {
                        this.loadCoplets(element,
                                         defaultCoplets,
                                         mediaCoplets,
                                         copletContents[1],
                                         processCopletsParallel,
                                         defaultCopletTimeout,
                                         statusProfile);
                    }
                }
                // END LOAD COPLETS

                // DESIGN
                // test for header
                if (copletContents[0].size() > 0) {
                    consumer.startElement("", "header", "header", attr);
                    this.processCopletList(copletContents[0], consumer, copletNotAvailableMessage, defaultCopletTimeout);
                    consumer.endElement("", "header", "header");
                }

                // content
                value = DOMUtil.getValueOfNode(miscNodes[PortalConstants.PROFILE_MISC_COLUMNS_NODE]);

                // for a simpler XSL-Stylesheet: The columns must be inserted in the
                // correct order!!!
                if (value != null && new Integer(value).intValue() > 0) {
                    attr.addAttribute("", "number", "number", "CDATA", value);
                    XMLUtils.startElement(consumer, "columns", attr);
                    attr.clear();

                    int columns = new Integer(value).intValue();
                    if (columns > PortalConstants.MAX_COLUMNS) {
                        throw new ProcessingException("portal: Maximum number of columns supported is: "+PortalConstants.MAX_COLUMNS);
                    }

                    // determine the width of the columns
                    String[] width = new String[columns];
                    int normalWidth = 100 / columns;
                    Element columnElement;

                    for(int colindex = 1; colindex <= columns; colindex++) {
                        columnElement = (Element)miscNodes[7 + colindex];
                        value = DOMUtil.getValueOf(columnElement, "width", this.xpathProcessor);
                        if (value == null) {
                            width[colindex-1] = "" + normalWidth + "%";
                        } else {
                            width[colindex-1] = value;
                        }
                    }

                    for(int colindex = 1; colindex <= columns; colindex++) {
                        attr.addAttribute("", "position", "position", "CDATA", "" + colindex);
                        attr.addAttribute("", "width", "width", "CDATA", width[colindex-1]);
                        XMLUtils.startElement(consumer, "column", attr);
                        attr.clear();

                        this.processCopletList(copletContents[colindex+1], consumer, copletNotAvailableMessage, defaultCopletTimeout);

                        XMLUtils.endElement(consumer, "column");
                    }
                    XMLUtils.endElement(consumer, "columns");
                } else {
                    attr.addAttribute("", "number", "number", "CDATA", "0");
                    XMLUtils.startElement(consumer, "columns", attr);
                    XMLUtils.endElement(consumer, "columns");
                    attr.clear();
                }

                // test for footer
                if (copletContents[1].size() > 0) {
                    XMLUtils.startElement(consumer, "footer");
                    this.processCopletList(copletContents[1], consumer, copletNotAvailableMessage, defaultCopletTimeout);
                    XMLUtils.endElement(consumer, "footer");
                }
                // END DESIGN

                for(int i=0; i<copletContents.length;i++) {
                    if (copletContents[i]!=null) copletContents[i].clear();
                }

                // Personal information and status information
                this.sendEvents(consumer, DOMUtil.getFirstNodeFromPath(profile, new String[] {"profile","personal-profile"}, false));
                this.sendEvents(consumer, statusProfile);
            }

            if (configMode) {
                XMLUtils.endElement(consumer, PortalConstants.ELEMENT_PORTALCONF);
            } else {
                XMLUtils.endElement(consumer, PortalConstants.ELEMENT_PORTAL);
            }

        } catch (javax.xml.transform.TransformerException local) { // end synchronized
            throw new ProcessingException("TransformerException: " + local, local);
        } finally {
            this.getTransactionManager().stopReadingTransaction(context);
        }
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END showPortal");
        }
    }


    /**
     * Building the profile.
     * This includes several steps which are declared in detail inside this method...
     */
    protected void buildProfile(String type,
                             String role,
                             String id,
                             boolean adminProfile)
    throws ProcessingException, IOException, SAXException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN buildProfile type=" + type + ", role=" + role + ", id=" +id+", adminProfile="+adminProfile);
        }
        try {
            // check parameter
            if (type == null) {
                throw new ProcessingException("buildProfile: Type is required");
            }
            if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL) ||
                type.equals(PortalManagerImpl.BUILDTYPE_VALUE_BASIC)) {
                // nothing to do here
            } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ROLE)) {
                if (role == null) {
                    throw new ProcessingException("buildProfile: Role is required");
                }
            } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
                if (role == null) {
                    throw new ProcessingException("buildProfile: Role is required");
                }
                if (id == null) {
                    throw new ProcessingException("buildProfile: ID is required");
                }
            } else {
                throw new ProcessingException("buildProfile: Type unknown: " + type);
            }

            SessionContext context = this.getContext(true);
            try {
                this.getTransactionManager().startWritingTransaction(context);

                String profileID = this.getProfileID(type, role, id, adminProfile);
                Map theProfile = null;

                // get the configuration
                Map config = this.getConfiguration();
                if (config == null) {
                    throw new ProcessingException("No configuration for portal found.");
                }

                // is the ID profile cached?
                if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID) ) {
                    theProfile = this.getCachedProfile(profileID, config);
                }

                if (theProfile == null) {

                    boolean doBase = false;
                    boolean doGlobal = false;
                    boolean doRole = false;
                    boolean doID = false;
                    String previousID;

                    if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
                        doID = true;
                        previousID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ROLE, role, null, adminProfile);
                        theProfile = this.getCachedProfile(previousID, config);
                        if (theProfile == null) {
                            doRole = true;
                            previousID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, adminProfile);
                            theProfile = this.getCachedProfile(previousID, config);
                            if (theProfile == null) {
                                doGlobal = true;
                                previousID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_BASIC, null, null, adminProfile);
                                theProfile = this.getCachedProfile(previousID, config);
                            }
                        }
                    } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ROLE)) {
                        theProfile = this.getCachedProfile(profileID, config);
                        if (theProfile == null) {
                            doRole = true;
                            previousID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, adminProfile);
                            theProfile = this.getCachedProfile(previousID, config);
                            if (theProfile == null) {
                                doGlobal = true;
                                previousID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_BASIC, null, null, adminProfile);
                                theProfile = this.getCachedProfile(previousID, config);
                            }
                        }
                    } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL)) {
                        theProfile = this.getCachedProfile(profileID, config);
                        if (theProfile == null) {
                            doGlobal = true;
                            previousID = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_BASIC, null, null, adminProfile);
                            theProfile = this.getCachedProfile(previousID, config);
                        }
                    } else { // basic profile
                        theProfile = this.getCachedProfile(profileID, config);
                    }

                    // build the profile
                    if (theProfile == null) {
                        theProfile = new HashMap(8,2);
                        doBase = true;
                    }

                    Element          profileRoot;
                    DocumentFragment profile;

                    if (doBase) {
                        // build the base level
                        profile = this.buildBaseProfile(config, adminProfile);
                        profileRoot = (Element)profile.getFirstChild();
                        theProfile.put(PortalConstants.PROFILE_PROFILE, profile);
                        this.cacheProfile(this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_BASIC, null, null, adminProfile), theProfile, config);
                    } else {
                        profile = (DocumentFragment)theProfile.get(PortalConstants.PROFILE_PROFILE);
                        profileRoot = (Element)profile.getFirstChild();
                    }

                    // load the global delta if type is global, role or user (but not basic!)
                    if (doGlobal) {
                        this.buildGlobalProfile(profileRoot, config, adminProfile);
                        this.cacheProfile(this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, adminProfile), theProfile, config);
                    }

                    // load the role delta if type is role or user
                    if (doRole) {
                        this.buildRoleProfile(profileRoot, config, role, adminProfile);
                        this.cacheProfile(this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ROLE, role, null, adminProfile), theProfile, config);
                    }

                    // load the user delta if type is user
                    if (doID) {
                        this.buildUserProfile(profileRoot, config, role, id, adminProfile);
                    }

                    // load the status profile when type is user
                    if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
                        this.buildUserStatusProfile(profileRoot, config, role, id);
                    }

                    if (!type.equals(PortalManagerImpl.BUILDTYPE_VALUE_BASIC)) {
                        this.buildRunProfile(theProfile, context, profile);

                        theProfile.put(PortalConstants.PROFILE_PORTAL_LAYOUTS,
                               this.buildPortalLayouts(context, profile));
                        theProfile.put(PortalConstants.PROFILE_COPLET_LAYOUTS,
                               this.buildcopleyLayouts(context, profile));

                        this.buildTypeProfile(theProfile, context, profile);
                    }

                    // cache the profile, if user
                    if (doID) {
                        this.cacheProfile(profileID, theProfile, config);
                    }
                } else {
                    // load the status profile when type is user
                    if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
                        DocumentFragment profile = (DocumentFragment)theProfile.get(PortalConstants.PROFILE_PROFILE);
                        Element profileRoot = (Element)profile.getFirstChild();
                        this.buildUserStatusProfile(profileRoot, config, role, id);
                    }
                }

                // store the whole profile
                this.storeProfile(profileID, theProfile);

                // now put role and id into the context if type is ID
                if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)
                    && !adminProfile) {
                    context.setAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE, role);
                    context.setAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ID, id);
                }
            } finally {
                this.getTransactionManager().stopWritingTransaction(context);
            }// end synchronized
        } catch (javax.xml.transform.TransformerException local) {
            throw new ProcessingException("TransformerException: " + local, local);
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildProfile");
        }
    }

    /**
     * Build the profile delta
     */
    private DocumentFragment buildProfileDelta(String type,
                                               String role,
                                               String id,
                                               boolean adminProfile)
    throws SAXException, ProcessingException, IOException, javax.xml.transform.TransformerException {
        // calling method must be synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildProfileDeltaN type="+type+", role="+role+", id="+id);
        }

        Map    originalProfile;
        Map    baseProfile;
        String baseType, baseRole, baseID, rootElementName;
        DocumentFragment originalFragment;
        DocumentFragment delta;
        SessionContext context = this.getContext(true);

        originalProfile = this.retrieveProfile(this.getProfileID(type, role, id, adminProfile));
        if (originalProfile == null) {
            throw new ProcessingException("buildProfileDelta: no profile found for " +
                   type + " - " + role + " - " + id + ".");
        }

        if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
            baseType = PortalManagerImpl.BUILDTYPE_VALUE_ROLE;
            baseRole = role;
            baseID   = null;
            rootElementName = "user-delta";
        } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ROLE)) {
            baseType = PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL;
            baseRole = null;
            baseID   = null;
            rootElementName = "role-delta";
        } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL)) {
            baseType = PortalManagerImpl.BUILDTYPE_VALUE_BASIC;
            baseRole = null;
            baseID   = null;
            rootElementName = "global-delta";
        } else {
            throw new ProcessingException("buildProfileDelta: type '"+type+"' not allowed.");
        }

        // the profile is created as we dont want to use any memory representation!
        this.createProfile(context, baseType, baseRole, baseID, adminProfile);
        baseProfile = this.retrieveProfile(this.getProfileID(baseType, baseRole, baseID, adminProfile));
        if (baseProfile == null) {
            throw new ProcessingException("buildProfileDelta: no baseProfile found.");
        }

        originalFragment = (DocumentFragment)originalProfile.get(PortalConstants.PROFILE_PROFILE);
        delta = originalFragment.getOwnerDocument().createDocumentFragment();
        delta.appendChild(delta.getOwnerDocument().createElementNS(null, rootElementName));

        // Copy portal content
        Node profileDelta = DOMUtil.getFirstNodeFromPath(originalFragment, new String[] {"profile","portal-profile"}, false).cloneNode(true);
        delta.getFirstChild().appendChild(profileDelta);

        // Diff layout profile, coplet profile, personal profile but not status profile!
        this.diff(originalFragment,
                 (DocumentFragment)baseProfile.get(PortalConstants.PROFILE_PROFILE),
                  "profile/layout-profile",
                  (Element)delta.getFirstChild());
        this.diff(originalFragment,
                  (DocumentFragment)baseProfile.get(PortalConstants.PROFILE_PROFILE),
                  "profile/coplets-profile",
                  (Element)delta.getFirstChild());
        if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL)) {
            profileDelta = DOMUtil.getFirstNodeFromPath(originalFragment, new String[] {"profile","personal-profile"}, false).cloneNode(true);
            delta.getFirstChild().appendChild(profileDelta);
        } else {
            this.diff(originalFragment,
                  (DocumentFragment)baseProfile.get(PortalConstants.PROFILE_PROFILE),
                  "profile/personal-profile",
                  (Element)delta.getFirstChild());
        }

        // check for the highes coplet number
        Node[] miscNodes = (Node[])originalProfile.get(PortalConstants.PROFILE_MISC_POINTER);
        Element lastCoplet = (Element)miscNodes[PortalConstants.PROFILE_MISC_LAST_COPLET_NODE];
        if (lastCoplet != null) {
            String lastNumber = lastCoplet.getAttributeNS(null, "number");
            if (lastNumber != null) {
                int value = new Integer(lastNumber).intValue();
                if (value > 1000000) {
                    NodeList coplets = DOMUtil.selectNodeList(delta, "profile/portal-profile/descendant::coplet[@id and @number]", this.xpathProcessor);
                    if (coplets != null) {
                        Element copletNode;
                        String oldNumber;
                        String copletId;
                        Element statusNode;
                        boolean copletsChanged = false;
                        for(int i=0; i <coplets.getLength(); i++) {
                            copletNode = (Element)coplets.item(i);
                            oldNumber = copletNode.getAttributeNS(null, "number");
                            copletId = copletNode.getAttributeNS(null, "id");
                            statusNode = (Element)DOMUtil.getSingleNode(delta, "status-profile/customization/coplet[@id='"+copletId+"' and @number='"+oldNumber+"']", this.xpathProcessor);
                            copletNode.setAttributeNS(null, "number", ""+(i+1));
                            if (statusNode != null) {
                                statusNode.setAttributeNS(null, "number", ""+(i+1));
                                copletsChanged = true;
                            }
                        }
                        if (copletsChanged) {
                            this.saveUserStatusProfile(originalProfile,
                                   this.getConfiguration(), role, id);
                        }
                    }
                }
            }
        }

        // Last part: strip type information
        NodeList typeElements = DOMUtil.selectNodeList(delta, "descendant::*[@formpath and @formdescription and @formtype]", this.xpathProcessor);
        if (typeElements != null) {
            for(int i = 0; i < typeElements.getLength(); i++) {
                ((Element)typeElements.item(i)).removeAttributeNS(null, "formpath");
                ((Element)typeElements.item(i)).removeAttributeNS(null, "formdescription");
                ((Element)typeElements.item(i)).removeAttributeNS(null, "formtype");
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildProfileDelta delta="+delta);
        }
        return delta;
    }

    /**
     * Make the difference :-)
     */
    private void diff(DocumentFragment original,
                      DocumentFragment base,
                      String           path,
                      Element          deltaElement)
    throws SAXException, javax.xml.transform.TransformerException {
        // calling method is already synchronized
        Element originalRoot = (Element)DOMUtil.getSingleNode(original, path, this.xpathProcessor);
        Element baseRoot = (Element)DOMUtil.getSingleNode(base, path, this.xpathProcessor);
        if (originalRoot != null && baseRoot != null) {
            List nodeStack = new ArrayList();
            String name = baseRoot.getNodeName();
            name = name.substring(0, name.indexOf("-profile")) + "-delta";
            nodeStack.add(originalRoot.getOwnerDocument().createElementNS(null, name));

            this.diffNode(baseRoot, originalRoot, nodeStack, deltaElement);
        }
    }

    /**
     * Diff one node
     */
    private void diffNode(Element baseNode,
                          Element originalNode,
                          List nodeStack,
                          Element deltaElement)
    throws SAXException, javax.xml.transform.TransformerException {
        // calling method is already synchronized
        NodeList baseChilds;
        NodeList originalChilds;
        int      i, len;
        int      m, l;
        boolean  found;
        Node     currentOrigNode = null;
        Node     currentBaseNode = null;

        originalChilds = originalNode.getChildNodes();
        len = originalChilds.getLength();
        baseChilds = baseNode.getChildNodes();
        l = baseChilds.getLength();

        for(i = 0; i < len; i++) {
            currentOrigNode = originalChilds.item(i);
            if (currentOrigNode.getNodeType() == Node.ELEMENT_NODE) {

                // search the delta node in the profile
                m = 0;
                found = false;
                while (!found && m < l) {
                    currentBaseNode = baseChilds.item(m);
                    if (currentBaseNode.getNodeType() == Node.ELEMENT_NODE
                        && currentBaseNode.getNodeName().equals(currentOrigNode.getNodeName()) ) {

                        // now we have found a node with the same name
                        // next: the attributes must match also
                        found = this.compareAttributes(currentBaseNode, currentOrigNode);
                    }
                    if (!found) m++;
                }

                if (found) {
                    // do we have elements as children or text?
                    currentOrigNode.normalize();
                    if (currentOrigNode.hasChildNodes()) {

                        // do a recursive call for sub elements
                        nodeStack.add(currentOrigNode);
                        this.diffNode((Element)currentBaseNode,
                                      (Element)currentOrigNode,
                                      nodeStack,
                                      deltaElement);

                        // and now compare the text nodes
                        String baseString = DOMUtil.getValueOfNode(currentBaseNode, "").trim();
                        String originalString = DOMUtil.getValueOfNode(currentOrigNode, "").trim();

                        if (!baseString.equals(originalString)) {
                            // this is the tricky part:
                            // we have to process all nodes on the stack
                            // and insert them in the deltaElement
                            Element currentElement;
                            Element contextElement = deltaElement;
                            NodeList possibleChilds;
                            boolean  foundChild;
                            int      cIndex;

                            for(int p = 0; p < nodeStack.size(); p++) {
                                currentElement = (Element)nodeStack.get(p);
                                possibleChilds = DOMUtil.getNodeListFromPath(contextElement, new String[] {currentElement.getNodeName()});
                                foundChild = false;
                                cIndex = 0;
                                if (possibleChilds != null) {
                                    while (!foundChild && cIndex < possibleChilds.getLength()) {
                                        foundChild = this.compareAttributes(currentElement, possibleChilds.item(cIndex));
                                        if (!foundChild) cIndex++;
                                    }
                                }
                                if (foundChild) {
                                    contextElement = (Element)possibleChilds.item(cIndex);
                                } else {
                                    currentElement = (Element)currentElement.cloneNode(false);
                                    contextElement.appendChild(currentElement);
                                    contextElement = currentElement;
                                }
                            }
                            // now add the text
                            contextElement.appendChild(contextElement.getOwnerDocument().createTextNode(originalString));
                        }

                        nodeStack.remove(nodeStack.size()-1);
                    }
                }
            }

        }

    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#getProfileID(java.lang.String, java.lang.String, java.lang.String, boolean)
     */
    public String getProfileID(String type,
                                String role,
                                String id,
                                boolean adminProfile)
    throws ProcessingException {
        // No sync required
        this.setup();
        StringBuffer key = new StringBuffer((adminProfile ? "aprofile:" : "uprofile:"));
        RequestState reqstate = this.getRequestState();
        key.append(reqstate.getHandlerName())
           .append('|')
           .append(reqstate.getApplicationName())
           .append(':')
           .append(type);

        if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ROLE)
            || type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
            role = XMLUtil.encode(role);
            key.append('_').append(role.length()).append('_').append(role);
        }
        if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
            id = XMLUtil.encode(id);
            key.append('_').append(id);
        }
        return key.toString();
    }

    /**
     * Get the profile role from the key
     */
    private boolean getIsAdminProfile(String profileID) {
        // No sync required
        return profileID.startsWith("a");
    }

    /**
     * Get the profile role from the key
     */
    private String getRole(String profileID) {
        // No sync required
        profileID = XMLUtil.decode(profileID);
        int pos = profileID.indexOf('_');
        if (pos == -1) {
            return null;
        } else {
            String lastPart = profileID.substring(pos+1);
            pos = lastPart.indexOf('_');
            if (pos == -1) return null;
            int len = new Integer(lastPart.substring(0, pos)).intValue();
            lastPart = lastPart.substring(pos+1, pos+1+len);
            return lastPart;
        }
    }

    /**
     * Get the profile ID from the key
     */
    private String getID(String profileID) {
        // No sync required
        profileID = XMLUtil.decode(profileID);
        int pos = profileID.indexOf('_');
        if (pos == -1) {
            return null;
        } else {
            String lastPart = profileID.substring(pos+1);
            pos = lastPart.indexOf('_');
            if (pos == -1) {
                return null;
            } else {
                lastPart = lastPart.substring(pos+1);
                pos = lastPart.indexOf('_');
                if (pos == -1) {
                    return null;
                } else {
                    return lastPart.substring(pos+1);
                }
            }
        }
    }

    /**
     * Get the profile type from the key
     */
    private String getType(String profileID) {
        // No sync required
        profileID = XMLUtil.decode(profileID);
        int endPos = profileID.indexOf('_');
        if (endPos == -1) {
            int startPos = profileID.lastIndexOf(':');
            return profileID.substring(startPos+1);
        } else {
            int startPos = profileID.lastIndexOf(':', endPos);
            return profileID.substring(startPos+1, endPos);
        }
    }

    /**
     * Store the profil
     */
    private void storeProfile(String profileID,
                              Map profile)
    throws ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN storeProfile id="+profileID+", profile="+profile);
        }

        Session session = this.getSessionManager().getSession(true);
        synchronized(session) {
            session.setAttribute(profileID, profile);
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END storeProfile");
        }
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#retrieveProfile(java.lang.String)
     */
    public Map retrieveProfile(String profileID)
    throws ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN retrieveProfile id="+profileID);
        }
        this.setup();
        Session session = this.getSessionManager().getSession(true);
        Map result;
        synchronized(session) {
            result = (Map)session.getAttribute(profileID);
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END retrieveProfile profile="+(result != null ? "**PROFILE**" : "null"));
        }

        return result;
    }

    /**
     * Cache the profile (if cache is turned on)
     */
    private void cacheProfile(String profileID,
                              Map profile,
                              Map configuration) {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN cacheProfile id="+profileID+", profile="+profile);
        }
        try {
            if (configuration != null && !this.getIsAdminProfile(profileID)) {
                String storePrefix = (String)configuration.get(PortalConstants.CONF_PROFILE_CACHE);
                if (storePrefix != null) {
                    String key = profileID.substring(1);
                    this.getProfileStore().store(key, profile);
                }
            }
        } catch (Exception local) {
            this.getLogger().warn("Caching Profile failed.", local);
            // local exceptions are ignored
            // we dont want to get an exception response due to cache problems
        }
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END cacheProfile");
        }
    }

    /**
     * Retrieve the cached profil if available
     */
    private Map getCachedProfile(String profileID, Map configuration) {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN getCachedProfile id="+profileID);
        }

        Map result = null;

        try {
            if (configuration != null && !this.getIsAdminProfile(profileID)) {
                final String storePrefix = (String)configuration.get(PortalConstants.CONF_PROFILE_CACHE);
                if (storePrefix != null) {
                    final String key = profileID.substring(1);
                    final Store store = this.getProfileStore();
                    if (store.containsKey(key)) {
                        result = (Map)store.get(key);
                    }
                }
            }
        } catch (Exception local) {
            // local exceptions are ignored
            // we dont want to get an exception response due to cache problems
            this.getLogger().warn("Getting cached Profile failed.", local);
            result = null;
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END getCachedProfile profile="+(result != null ? "**PROFILE**" : "null"));
        }
        return result;
    }

    /**
     * Clean up the cache, if the global profile was saved, delete all role and user profiles.
     * If a role profile was saved delete all user profiles. If the basic profile was
     * saved delete all profiles.
     */
    private void cleanUpCache(String type, String role, Map configuration)
    throws ProcessingException {
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN cleanUpCache type="+type+", role="+role+", config="+configuration);
        }
        if (configuration != null
            && type != null
            && !type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
            String storePrefix = (String)configuration.get(PortalConstants.CONF_PROFILE_CACHE);
            if (storePrefix != null) {
                Store store = this.getProfileStore();
                Enumeration keys = store.keys();
                String   currentKey;
                String  deleteGlobal = null;
                String  deleteRole = null;
                String  deleteUser = null;

                if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_BASIC) ||
                    type.equals(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL)) {
                    if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_BASIC)) {
                        deleteGlobal = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, false).substring(1);
                    }
                    deleteRole = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, false);
                    deleteRole = deleteRole.substring(1, deleteRole.lastIndexOf(':')+1) + PortalManagerImpl.BUILDTYPE_VALUE_ROLE;
                    deleteUser = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, false);
                    deleteUser = deleteUser.substring(1, deleteUser.lastIndexOf(':')+1) + PortalManagerImpl.BUILDTYPE_VALUE_ID;
                } else { // role
                    deleteGlobal = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ROLE, role, null, false).substring(1);
                    deleteUser = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ID, role, "a", false);
                    deleteUser = deleteUser.substring(1, deleteUser.length()-1);
                }

                while (keys.hasMoreElements()) {
                    Object k = keys.nextElement();
                    if ( k instanceof String ) {
                        currentKey = (String)k;
                        if (deleteGlobal != null && currentKey.equals(deleteGlobal)) {
                            store.remove(currentKey);
                        } else if (deleteRole != null && currentKey.startsWith(deleteRole)) {
                            store.remove(currentKey);
                        } else if (deleteUser != null && currentKey.startsWith(deleteUser)) {
                            store.remove(currentKey);
                        }
                    }
                }
            }
        } else if (configuration != null && type == null) {
            // clean whole cache
            String storePrefix = (String)configuration.get(PortalConstants.CONF_PROFILE_CACHE);
            if (storePrefix != null) {
                Store store = this.getProfileStore();
                Enumeration keys = store.keys();
                String currentKey;
                String delete;

                delete = this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL, null, null, false);
                delete = delete.substring(1, delete.lastIndexOf(':') + 1);
                while (keys.hasMoreElements()) {
                    Object k = keys.nextElement();
                    if ( k instanceof String ) {
                        currentKey = (String)k;
                        if (currentKey.startsWith(delete)) {
                            store.remove(currentKey);
                        }
                    }
                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END cleanUpCache");
        }
    }

    /**
     * Build the run profil and store it in the <code>profileMap</code>.
     */
    private void buildRunProfile(Map              profileMap,
                                 SessionContext  context,
                                 DocumentFragment baseProfile)
    throws ProcessingException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN buildRunProfile context="+context+", profile="+baseProfile);
        }

        // The map containing the coplets which appear on each medium
        Map defaultCoplets = new HashMap(20, 5);
        // The map containing for each media type a map with coplets which
        // appear only for the given media
        Map mediaCoplets   = new HashMap(5, 2);

        profileMap.put(PortalConstants.PROFILE_DEFAULT_COPLETS, defaultCoplets);
        profileMap.put(PortalConstants.PROFILE_MEDIA_COPLETS, mediaCoplets);

        // get AuthenticationManager instance
        String[] types = this.getMediaManager().getMediaTypes();
        Map      mediaMap;
        for(int i = 0; i < types.length; i++) {
            mediaCoplets.put(types[i], new HashMap(5, 3));
        }

        // build misc nodes
        Node[] miscNodes = new Node[13];
        miscNodes[PortalConstants.PROFILE_MISC_HEADER_NODE] = DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","layout-profile","portal","header","exists"}, false);
        miscNodes[PortalConstants.PROFILE_MISC_FOOTER_NODE] = DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","layout-profile","portal","footer","exists"}, false);
        miscNodes[PortalConstants.PROFILE_MISC_HEADER_CONTENT_NODE] = DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","portal-profile","content","header"}, false);
        miscNodes[PortalConstants.PROFILE_MISC_FOOTER_CONTENT_NODE] = DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","portal-profile","content","footer"}, false);
        miscNodes[PortalConstants.PROFILE_MISC_COLUMNS_NODE]= DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","layout-profile","portal","columns","number"}, false);
        miscNodes[PortalConstants.PROFILE_MISC_MESSAGES_NODE]= DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","personal-profile","messages"}, false);
        for(int i = 1; i <= PortalConstants.MAX_COLUMNS; i++) {
            miscNodes[7 + i] = DOMUtil.getSingleNode(baseProfile,
                "profile/portal-profile/content/column[@position='"+i+"']", this.xpathProcessor);
        }

        profileMap.put(PortalConstants.PROFILE_MISC_POINTER, miscNodes);

        // build coplet configs
        NodeList coplets;
        int      i, l;
        Element  configElement;
        String   copletID;
        String   copletMedia;

        coplets = DOMUtil.getNodeListFromPath(baseProfile, new String[] {"profile","coplets-profile","coplets","coplet"});

        if (coplets != null) {

            l = coplets.getLength();
            for(i = 0; i < l; i++) {
                configElement = (Element)coplets.item(i);
                if (DOMUtil.getValueAsBooleanOf(configElement, "configuration/active", this.xpathProcessor)) {

                    copletID = configElement.getAttributeNS(null, "id");
                    if (configElement.hasAttributeNS(null, "media")) {
                        copletMedia = configElement.getAttributeNS(null, "media");
                        mediaMap = (Map)mediaCoplets.get(copletMedia);
                        if (mediaMap != null) {
                            mediaMap.put(copletID, configElement);
                        }
                    } else {
                        copletMedia = null;
                        defaultCoplets.put(copletID, configElement);
                    }

                    // Now: add the coplet if mandatory and missing
                    if (DOMUtil.getValueAsBooleanOf(configElement, "configuration/mandatory", this.xpathProcessor)) {
                        // get all coplet instances
                        NodeList copletElements;

                        // the next is crap, but it works....
                        // search all coplets (columns, header, footer)
                        if (copletMedia == null) {
                            copletElements = DOMUtil.selectNodeList(baseProfile,
                                "profile/portal-profile/content/column/coplets/coplet[@id='"+copletID+"' and not(@media)]", this.xpathProcessor);
                        } else {
                            copletElements = DOMUtil.selectNodeList(baseProfile,
                                "profile/portal-profile/content/column/coplets/coplet[@id='"+copletID+"' and media='"+copletMedia+"']", this.xpathProcessor);
                        }

                        if (copletElements == null || copletElements.getLength() == 0) {
                            if (copletMedia == null) {
                                copletElements = DOMUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/header/coplet[@id='"+copletID+"' and not(@media)]", this.xpathProcessor);
                            } else {
                                copletElements = DOMUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/header/coplet[@id='"+copletID+"' and media='"+copletMedia+"']", this.xpathProcessor);
                            }
                        }

                        if (copletElements == null || copletElements.getLength() == 0) {
                            if (copletMedia == null) {
                                copletElements = DOMUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/footer/coplet[@id='"+copletID+"' and not(@media)]", this.xpathProcessor);
                            } else {
                                copletElements = DOMUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/footer/coplet[@id='"+copletID+"' and media='"+copletMedia+"']", this.xpathProcessor);
                            }
                        }

                        if (copletElements == null || copletElements.getLength() == 0) {
                            // mandatory coplet is not configured, so add it to the first column
                            Node content = DOMUtil.getSingleNode(baseProfile,
                                  "profile/portal-profile/content/column[@position='1']/coplets", this.xpathProcessor);
                            if (content == null)
                                throw new ProcessingException("Element not found: portal-profile/content/column/coplets");
                            Element el = content.getOwnerDocument().createElementNS(null, "coplet");
                            el.setAttributeNS(null, "id", copletID);
                            if (copletMedia != null) {
                                el.setAttributeNS(null, "media", copletMedia);
                            }
                            // Set position attribute
                            NodeList childs = DOMUtil.getNodeListFromPath(content, new String[] {"coplet"});
                            int      childsCount = (childs == null ? 0 : childs.getLength());
                            el.setAttributeNS(null, "position", ""+(childsCount+1));
                            Text    t;
                            content.appendChild(el);
                            content = el;
                            el = content.getOwnerDocument().createElementNS(null, "status");
                            content.appendChild(el);
                            content = el;
                            el = content.getOwnerDocument().createElementNS(null, "visible");
                            content.appendChild(el);
                            content = el;
                            t = content.getOwnerDocument().createTextNode("true");
                            content.appendChild(t);
                        } else {
                            // is any of them visible?
                            boolean found;
                            boolean origVisible = DOMUtil.getValueAsBooleanOf(configElement, "status/visible", this.xpathProcessor);
                            int si, sl;
                            sl = copletElements.getLength();
                            si = 0;
                            found = false;
                            while (si < sl && !found) {
                                found = DOMUtil.getValueAsBooleanOf(copletElements.item(si),
                                           "status/visible", origVisible, this.xpathProcessor);
                                si++;
                            }
                            if (!found) {
                                // set first to visible
                                // first: is status node available
                                Node statusElem = DOMUtil.getFirstNodeFromPath(copletElements.item(0), new String[] {"status"}, false);
                                if (statusElem == null) {
                                    statusElem = copletElements.item(0).getOwnerDocument().createElementNS(null, "status");
                                    copletElements.item(0).appendChild(statusElem);
                                }
                                // second: is visible node available
                                Node visibleElem = DOMUtil.getFirstNodeFromPath(statusElem, new String[] {"visible"}, false);
                                if (visibleElem == null) {
                                    visibleElem = statusElem.getOwnerDocument().createElementNS(null, "visible");
                                    statusElem.appendChild(visibleElem);
                                }
                                // remove old childs
                                while (visibleElem.hasChildNodes()) {
                                    visibleElem.removeChild(visibleElem.getFirstChild());
                                }
                                visibleElem.appendChild(statusElem.getOwnerDocument().createTextNode("true"));
                            }
                        }
                    }
                }
            }
        }

        // Numerate all coplets by adding an attribute number with a unique value
        // and put them into the corresponding maps.
        // update the status section of the coplet: Only the values of the coplet
        // configuration are allowed. Not less and not more!
        // All coplets are required to have
        // the number attribute! So this is only a compatibility function
        // which adds the first time the number to the coplets
        // If the number attribute is available, the node with the highest
        // number is searched
        NodeList copletElements;
        int     number = 0;
        Element  content = (Element)DOMUtil.getFirstNodeFromPath(baseProfile,
                       new String[] {"profile","portal-profile","content"}, false);
        Element  currentCoplet;
        NodeList statusConfigList;
        NodeList statusCopletList;
        Element  statusCopletElement;
        int      list_index, list_length;
        Node     currentStatus;
        int      highestCopletNumber = -1;

        for(i = 0; i < 7; i++) {
            if (i == 0) {
                copletElements = DOMUtil.getNodeListFromPath(content,
                   new String[] {"header","coplet"});
            } else if (i == 1) {
                copletElements = DOMUtil.getNodeListFromPath(content,
                   new String[] {"footer","coplet"});
            } else {
                copletElements = DOMUtil.selectNodeList(content,
                   "column[@position='"+(i-1)+"']/coplets/coplet", this.xpathProcessor);
            }
            if (copletElements != null && copletElements.getLength() > 0) {
                Element[] list = new Element[copletElements.getLength()];
                for(int index = 0; index < copletElements.getLength(); index++) {
                    list[index] = (Element)copletElements.item(index);
                }

                for(int index = 0; index < list.length; index++) {
                    // get coplet element
                    currentCoplet = list[index];

                    String numberValue = currentCoplet.getAttributeNS(null, "number");
                    if (numberValue == null || numberValue.length() == 0) {
                        // create unique number attribute
                        currentCoplet.setAttributeNS(null, "number", ""+number);
                        miscNodes[PortalConstants.PROFILE_MISC_LAST_COPLET_NODE] = currentCoplet;
                        number++;
                    } else {
                        int currentNumber = new Integer(numberValue).intValue();
                        if (currentNumber > highestCopletNumber) {
                            highestCopletNumber = currentNumber;
                            number = highestCopletNumber+1;
                            miscNodes[PortalConstants.PROFILE_MISC_LAST_COPLET_NODE] = currentCoplet;
                        }
                    }
                    // update status
                    configElement = this.getCopletConfiguration(currentCoplet.getAttributeNS(null, "id"),
                                                      defaultCoplets,
                                                      mediaCoplets);
                    if (configElement != null) {
                        statusCopletElement = (Element)DOMUtil.selectSingleNode(configElement, "status", this.xpathProcessor);
                        statusConfigList = DOMUtil.selectNodeList(statusCopletElement, "*", this.xpathProcessor);
                        statusCopletList = DOMUtil.selectNodeList(currentCoplet, "status/*", this.xpathProcessor);
                        // first test if each status is included in the config
                        if (statusCopletList != null) {
                            list_length = statusCopletList.getLength();
                            for(list_index = list_length-1; list_index >= 0; list_index--) {
                                currentStatus = statusCopletList.item(list_index);
                                if (currentStatus.getNodeType() == Node.ELEMENT_NODE) {
                                    if (DOMUtil.getFirstNodeFromPath(configElement, new String[] {"status", currentStatus.getNodeName()}, false) == null) {
                                        currentStatus.getParentNode().removeChild(currentStatus);
                                    }
                                }
                            }
                        }
                        // second, test if each status attribute of the config is included
                        if (statusConfigList != null) {
                            list_length = statusConfigList.getLength();
                            for(list_index = 0; list_index < list_length; list_index++) {
                                currentStatus = statusConfigList.item(list_index);
                                if (currentStatus.getNodeType() == Node.ELEMENT_NODE) {
                                    if (DOMUtil.getFirstNodeFromPath(statusCopletElement, new String[] {currentStatus.getNodeName()}, false) == null) {
                                        // create a new element
                                        statusCopletElement.appendChild(statusCopletElement.getOwnerDocument().importNode(currentStatus, true));
                                    }
                                }
                            }
                        }
                    } else {
                        // coplet not in configuration
                        // adopt position of following coplets and then remove
                        String posAttr = currentCoplet.getAttributeNS(null, "position");
                        NodeList followUps = DOMUtil.selectNodeList(currentCoplet.getParentNode(), "coplet[@position > '"+posAttr+"']", this.xpathProcessor);
                        if (followUps != null) {
                            int value;
                            for(int iq = 0; iq < followUps.getLength(); iq++) {
                                value = new Integer(((Element)followUps.item(iq)).getAttributeNS(null, "position")).intValue();
                                value -= 1;
                                ((Element)followUps.item(iq)).setAttributeNS(null, "position", "" + value);
                            }
                        }
                        currentCoplet.getParentNode().removeChild(currentCoplet);
                    }
                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildRunProfile");
        }
    }

    /**
     *  Add the type information to the profile and do some type checkings
     */
    private void buildTypeProfile(Map              theProfile,
                                  SessionContext  context,
                                  DocumentFragment baseProfile)
    throws javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN buildTypeProfile context="+context+", profile="+baseProfile);
        }
        List list = new ArrayList(25);
        List confList = new ArrayList(25);

        theProfile.put(PortalConstants.PROFILE_TYPE_PATHS, list);
        theProfile.put(PortalConstants.PROFILE_TYPE_CONF_PATHS, confList);

        Element typeElement;

        typeElement = (Element)DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","type-profile","elements"}, false);
        if (typeElement != null) {
            if (typeElement.hasChildNodes())
                this.addTypePath(list, typeElement.getChildNodes(), "profile");

            // now we have the list with the xpaths
            this.setTypeInfo(baseProfile, list, null);

            // build the conf paths
            int i, l, pos;
            String current;

            l = list.size();
            for(i = 0; i < l; i++) {
                current = (String)list.get(i);

                // now the path has to be changed: the new attributes must be included
                pos = current.lastIndexOf('/');
                current = current.substring(0, pos);

                pos = current.lastIndexOf('[');
                if (current.substring(pos+1).equals("not(@*)]")) {
                    current = current.substring(0, pos+1);
                } else {
                    current = current.substring(0, current.length()-1) + " and ";
                }
                current += "@formtype and @formpath and @formdescription]";
                confList.add(current);
            }

        }

        // and now the type checking part:
        //
        // If the default layout has changed the number of columns and the current
        // user (or role) is not allowed to change this, we have to adjust the
        // profile. Otherwise the current number of columns has to be stored
        // into the profile layout part.
        Element layoutColumnsNode = (Element)DOMUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","layout-profile","portal","columns","number"}, false);
        String layoutValue = DOMUtil.getValueOfNode(layoutColumnsNode);
        int layoutColumns = 0;
        if (layoutValue != null && new Integer(layoutValue).intValue() > 0) {
            layoutColumns = new Integer(layoutValue).intValue();
        }
        NodeList columnNodes = DOMUtil.selectNodeList(baseProfile, "profile/portal-profile/content/column[@position]", this.xpathProcessor);
        int columns = columnNodes.getLength();
        if (columns != layoutColumns) {
            if (layoutColumnsNode.hasAttributeNS(null, "formtype")) {
                DOMUtil.setValueOfNode(layoutColumnsNode, ""+columns);
            } else {
                this.changeColumns(baseProfile,
                                   columns,
                                   layoutColumns,
                                   (Node[])theProfile.get(PortalConstants.PROFILE_MISC_POINTER));
                this.setTypeInfo(baseProfile,
                                 (List)theProfile.get(PortalConstants.PROFILE_TYPE_PATHS),
                                 (List)theProfile.get(PortalConstants.PROFILE_TYPE_CONF_PATHS));
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildTypeProfile");
        }
    }

    /**
     * Set the tpe information
     */
    private void setTypeInfo(DocumentFragment baseProfile, List paths, List confPaths)
    throws javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN setTypeInfo profile="+baseProfile+", paths="+paths);
        }
        if (baseProfile != null && paths != null) {
            int pos;
            String currentPath;
            String value;
            String description;
            NodeList nodes;
            int nodes_count;
            int path_count = paths.size();
            Node currentNode;

            for(int i = 0; i < path_count; i++) {
                currentPath = (String)paths.get(i);
                pos = currentPath.lastIndexOf('/');
                value = currentPath.substring(pos + 1);
                currentPath = currentPath.substring(0, pos);
                pos = value.indexOf("|");
                if (pos != -1) {
                    description = value.substring(pos + 1);
                    value = value.substring(0, pos);
                } else {
                    description = "UNKNOWN";
                }

                // get all nodes
                boolean changed = false;
                nodes = DOMUtil.selectNodeList(baseProfile, currentPath, this.xpathProcessor);
                if (nodes != null) {
                    nodes_count = nodes.getLength();
                    for(int m = 0; m < nodes_count; m++) {
                        currentNode = nodes.item(m);
                        if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
                            ((Element)currentNode).setAttributeNS(null, "formtype", value);
                            ((Element)currentNode).setAttributeNS(null, "formpath",
                                     PortalManagerImpl.REQ_PARAMETER_CONF + '.' + i + '.' + m);
                            ((Element)currentNode).setAttributeNS(null, "formdescription", description);
                            changed = true;
                        }
                    }
                }
                if (changed && confPaths != null) {
                    currentPath = (String)confPaths.get(i);
                    nodes = DOMUtil.selectNodeList(baseProfile, currentPath, this.xpathProcessor);
                    if (nodes != null) {
                        nodes_count = nodes.getLength();
                        for(int m = 0; m < nodes_count; m++) {
                            currentNode = nodes.item(m);
                            if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
                                ((Element)currentNode).setAttributeNS(null, "formpath",
                                     PortalManagerImpl.REQ_PARAMETER_CONF + '.' + i + '.' + m);
                            }
                        }
                    }
                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END setTypeInfo");
        }
    }


    /**
     * Add the type info to the xpath. This is done recursevly
     */
    private void addTypePath(List list, NodeList childs, String path) {
        // calling method is synced
        int i, l;
        Element current;
        StringBuffer newPath;

        l = childs.getLength();
        for(i = 0; i < l; i++) {
            if (childs.item(i).getNodeType() == Node.ELEMENT_NODE) {
                current = (Element)childs.item(i);
                newPath = new StringBuffer(path);
                newPath.append('/').append(current.getNodeName());
                if (current.hasAttributes()) {
                    NamedNodeMap nnm = current.getAttributes();
                    int ia, la;
                    boolean first = true;
                    StringBuffer expression = new StringBuffer();
                    la = nnm.getLength();
                    newPath.append('[');
                    for(ia = 0; ia < la; ia++) {
                        if (!nnm.item(ia).getNodeName().equals("type")
                            && !nnm.item(ia).getNodeName().equals("description")) {
                            if (!first) expression.append(" and ");
                            if (!nnm.item(ia).getNodeValue().equals("*")) {
                                expression.append('@')
                                  .append(nnm.item(ia).getNodeName())
                                  .append("='")
                                  .append(nnm.item(ia).getNodeValue())
                                  .append("'");
                            } else {
                                expression.append('@').append(nnm.item(ia).getNodeName());
                            }
                            first = false;
                        }
                    }
                    if (first) {
                        newPath.append("not(@*)");
                    } else {
                        newPath.append(expression);
                    }
                    newPath.append(']');
                } else {
                    newPath.append("[not(@*)]");
                }
                if (current.getAttributeNS(null, "type").length() > 0) {
                    list.add(newPath.toString() + '/' + current.getAttributeNS(null, "type") + '|' + current.getAttributeNS(null, "description"));
                } else {
                    if (current.hasChildNodes()) {
                        this.addTypePath(list, current.getChildNodes(), newPath.toString());
                    }
                }
            }
        }
    }

    /**
     * Build the Map with the portal layouts
     */
    private Map buildPortalLayouts(SessionContext context,
                                 DocumentFragment baseProfile)
    throws ProcessingException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN buildPortalLayouts context="+context+", profile="+baseProfile);
        }
        Map layouts = new HashMap(5, 2);
        Element defLayout = (Element)DOMUtil.getSingleNode(baseProfile,
                        "profile/layout-profile/portal/layouts/layout[not(@*)]", this.xpathProcessor);
        Node currentLayout;
        String[] types = this.getMediaManager().getMediaTypes();

        for(int i = 0; i < types.length; i++) {
             currentLayout = DOMUtil.getSingleNode(baseProfile,
               "profile/layout-profile/portal/layouts/layout[media='"+types[i]+"']", this.xpathProcessor);
             layouts.put(types[i], (currentLayout == null ? defLayout : currentLayout));
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildPortalLayouts layouts="+layouts);
        }
        return layouts;
    }

    /**
     * Build the Map with the coplet layouts
     */
    private Map buildcopleyLayouts(SessionContext context,
                                 DocumentFragment baseProfile)
    throws ProcessingException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN buildcopleyLayouts context="+context+", profile="+baseProfile);
        }
        Map layouts = new HashMap(5, 2);
        Element defLayout = (Element)DOMUtil.getSingleNode(baseProfile,
                        "profile/layout-profile/coplets/layouts/layout[not(@*)]", this.xpathProcessor);
        Node currentLayout;
        String[] types = this.getMediaManager().getMediaTypes();

        for(int i = 0; i < types.length; i++) {
            currentLayout = DOMUtil.getSingleNode(baseProfile,
               "profile/layout-profile/coplets/layouts/layout[media='"+types[i]+"']", this.xpathProcessor);
           layouts.put(types[i], (currentLayout == null ? defLayout : currentLayout));
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildcopleyLayouts layouts="+layouts);
        }
        return layouts;
    }

    /**
     * Import a delta into the profile
     */
    private void importProfileDelta(Element          profileRoot,
                                  DocumentFragment delta,
                                  String           deltaRootTagName,
                                  String           deltaTag)
    throws ProcessingException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN importProfileDelta root=" + profileRoot + ", delta=" + delta + ", deltaRoot:" + deltaRootTagName + ", delta: " + deltaTag);
        }
        Node     deltaRoot   = null;

        deltaRoot = DOMUtil.getFirstNodeFromPath(delta, new String[] {deltaRootTagName, deltaTag}, false);

        if (deltaRoot != null) {
            // root tag found in delta , now search root tag in profile
            String searchName = deltaRoot.getNodeName().substring(0, deltaRoot.getNodeName().lastIndexOf("-delta"));
            searchName = searchName + "-profile";

            profileRoot = (Element)DOMUtil.getFirstNodeFromPath(profileRoot, new String[] {searchName}, false);
            if (profileRoot == null) {
                throw new ProcessingException("Importing Delta: Tag " + searchName + " not found in profile.");
            }

            // now import it
            this.importNode(profileRoot, (Element)deltaRoot);
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END importProfileDelta");
        }
    }

    /**
     * Add the node to the profile (replace an existing one)
     */
    private void addProfilePart(Element          profileRoot,
                              DocumentFragment delta,
                              String           deltaRootTagName,
                              String           deltaTag)
    {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
           this.getLogger().debug("BEGIN addProfilePart root=" + profileRoot + ", delta=" + delta + ", deltaRoot:" + deltaRootTagName + ", delta: " + deltaTag);
        }
        Node     deltaRoot   = null;
        Node     oldNode     = null;

        if (deltaRootTagName != null) {
            deltaRoot = DOMUtil.getFirstNodeFromPath(delta, new String[] {deltaRootTagName, deltaTag}, false);
        } else {
            deltaRoot = DOMUtil.getFirstNodeFromPath(delta, new String[] {deltaTag}, false);
        }

        if (deltaRoot != null) {
            // root tag found in delta found, now search root tag in profile
            oldNode = DOMUtil.getFirstNodeFromPath(profileRoot, new String[] {deltaTag}, false);
            if (oldNode == null) {
                profileRoot.appendChild(profileRoot.getOwnerDocument().importNode(deltaRoot, true));
            } else {
                profileRoot.replaceChild(profileRoot.getOwnerDocument().importNode(deltaRoot, true), oldNode);
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END addProfilePart");
        }
    }


    /**
     * This is the hardest part. Incorporting a node into the profile.
     * For performance reasons there is now tracing here.
     */
    private void importNode(Element profile, Element delta) {
        // calling method is synced
        NodeList profileChilds = null;
        NodeList deltaChilds   = delta.getChildNodes();
        int      i, len;
        int      m, l;
        boolean  found;
        Node     currentDelta = null;
        Node     currentProfile = null;

        len = deltaChilds.getLength();
        for(i = 0; i < len; i++) {
            currentDelta = deltaChilds.item(i);
            if (currentDelta.getNodeType() == Node.ELEMENT_NODE) {
                // search the delta node in the profile
                profileChilds = profile.getChildNodes();
                l = profileChilds.getLength();
                m = 0;
                found = false;
                while (!found && m < l) {
                    currentProfile = profileChilds.item(m);
                    if (currentProfile.getNodeType() == Node.ELEMENT_NODE
                        && currentProfile.getNodeName().equals(currentDelta.getNodeName())) {

                        // now we have found a node with the same name
                        // next: the attributes must match also
                        found = this.compareAttributes(currentProfile, currentDelta);
                    }
                    if (!found) m++;
                }
                if (found) {
                    // this is not new

                    // do we have elements as children or text?
                    if (currentDelta.hasChildNodes()) {
                        currentDelta.normalize();
                        currentProfile.normalize();
                        // do a recursive call for sub elements
                        this.importNode((Element)currentProfile, (Element)currentDelta);
                        // and now the text nodes: Remove all from the profile and add all
                        // of the delta
                        NodeList childs = currentProfile.getChildNodes();
                        int      index, max;
                        max = childs.getLength();
                        for(index = max - 1; index >= 0; index--) {
                            if (childs.item(index).getNodeType() == Node.TEXT_NODE) {
                                currentProfile.removeChild(childs.item(index));
                            }
                        }
                        childs = currentDelta.getChildNodes();
                        max = childs.getLength();
                        for(index = 0; index < max; index++) {
                            if (childs.item(index).getNodeType() == Node.TEXT_NODE) {
                                currentProfile.appendChild(currentProfile.getOwnerDocument()
                                     .createTextNode(childs.item(index).getNodeValue()));
                            }
                        }
                    }
                } else {
                    // this is a new node, so it is considered as an old information
                    // No inserting: profile.appendChild(profile.getOwnerDocument().importNode(currentDelta, true));
                }
            }

        }

    }

    /**
     * Compare Attributes of two nodes. This method returns true only if both
     * nodes have the same number of attributes and the same attributes with equal
     * values.
     * Namespacedefinition nodes are ignored
     * BUT: For type handling the attributes <code>formtype</code>,
     *      <code>formdescription</code> and <code>formpath</code> are ignored!
     */
    private boolean compareAttributes(Node first, Node second) {
        // calling method is synced
        NamedNodeMap attr1 = first.getAttributes();
        NamedNodeMap attr2 = second.getAttributes();
        String value;

        if (attr1 == null && attr2 == null) return true;
        if (attr1 == null || attr2 == null) return false;
        int attr1Len = (attr1 == null ? 0 : attr1.getLength());
        int attr2Len = (attr2 == null ? 0 : attr2.getLength());
        if (attr1Len > 0) {
            if (attr1.getNamedItemNS(null, "formtype") != null) attr1Len--;
            if (attr1.getNamedItemNS(null, "formpath") != null) attr1Len--;
            if (attr1.getNamedItemNS(null, "formdescription") != null) attr1Len--;
            int l = attr1.getLength();
            for(int i=0;i<l;i++) {
                if (attr1.item(i).getNodeName().startsWith("xmlns:"))
                    attr1Len--;
            }
        }
        if (attr2Len > 0) {
            if (attr2.getNamedItemNS(null, "formtype") != null) attr2Len--;
            if (attr2.getNamedItemNS(null, "formpath") != null) attr2Len--;
            if (attr2.getNamedItemNS(null, "formdescription") != null) attr2Len--;
            int l = attr2.getLength();
            for(int i=0;i<l;i++) {
                if (attr2.item(i).getNodeName().startsWith("xmlns:"))
                    attr2Len--;
            }
        }
        if (attr1Len != attr2Len) return false;
        int i, l;
        int m, l2;
        i = 0;
        l = attr1.getLength();
        l2 = attr2.getLength();
        boolean ok = true;
        // each attribute of first must be in second with the same value
        while (i < l && ok) {
            value = attr1.item(i).getNodeName();
            if (!value.equals("formtype")
                && !value.equals("formpath")
                && !value.equals("formdescription")
                && !value.startsWith("xmlns:")) {
                ok = false;
                m = 0;
                while (m < l2 && !ok) {
                    if (attr2.item(m).getNodeName().equals(value)) {
                        // same name, same value?
                        ok = attr1.item(i).getNodeValue().equals(attr2.item(m).getNodeValue());
                    }
                    m++;
                }

            }
            i++;
        }
        return ok;
    }


    /**
     * Parse the fragment(tree denoted by the element)
     * and include the processed xml in the output
     */
    private void processCopletList(List        copletList,
                                   XMLConsumer consumer,
                                   String      copletNotAvailableMessage,
                                   long        defaultCopletTimeout)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        for(int i = 0; i < copletList.size(); i++) {
            this.processCoplet((Object[])copletList.get(i),
                         consumer, copletNotAvailableMessage, defaultCopletTimeout);
        }
    }

    /**
     * Parse the fragment(tree denoted by the element)
     * and include the processed xml in the output
     */
    private void loadCoplets(Element element,
                             Map     defaultCoplets,
                             Map     mediaCoplets,
                             List    copletList,
                             boolean parallelCoplets,
                             long    defaultCopletTimeout,
                             Element statusProfile)
    throws ProcessingException, javax.xml.transform.TransformerException {
        // calling method is synced
        // All children, which are coplets are processed, all other tags
        // are ignored
        if (element.hasChildNodes()) {
            NodeList childs = element.getChildNodes();
            Node     current = null;
            int i, l;
            l = childs.getLength();
            for(i = 0; i < l; i++) {
                current = childs.item(i);
                if (current.getNodeType() == Node.ELEMENT_NODE
                    && current.getNodeName().equals("coplet")) {

                    // now we have a coplet
                    this.loadCoplet((Element)current,
                             defaultCoplets,
                             mediaCoplets,
                             copletList,
                             parallelCoplets,
                             defaultCopletTimeout,
                             statusProfile);
                }
            }
        }
    }

    /**
     * Load a coplet and store the binary output in the list
     */
    private void loadCoplet(Element         element,
                            Map             defaultCoplets,
                            Map             mediaCoplets,
                            List            copletList,
                            boolean         parallelCoplets,
                            long            defaultCopletTimeout,
                            Element         statusProfile)
    throws ProcessingException, javax.xml.transform.TransformerException {
        // calling method is synced
        String copletID = element.getAttributeNS(null, "id");

        Element copletConf = this.getCopletConfiguration(copletID, defaultCoplets, mediaCoplets);
        if (copletConf != null) {

             // first: check visibility
            boolean visible = DOMUtil.getValueAsBooleanOf(element,
                "status/visible", this.xpathProcessor);
            // second: check media
            String media = this.getMediaManager().getMediaType();
            if (visible && copletConf.hasAttributeNS(null, "media")) {
                String copletMedia = copletConf.getAttributeNS(null, "media");
                visible = media.equals(copletMedia);
            }

            if (visible) {

                Object[] loadedCoplet = new Object[8];
                copletList.add(loadedCoplet);

                boolean isCustomizable = DOMUtil.getValueAsBooleanOf(copletConf, "configuration/customizable", false, this.xpathProcessor);
                if (isCustomizable) {
                    boolean showCustomizePage = DOMUtil.getValueAsBooleanOf(element, "status/customize", false, this.xpathProcessor);
                    boolean hasConfig = false;
                    if (statusProfile != null) {
                        Element customInfo = (Element)DOMUtil.getSingleNode(statusProfile,
                              "customization/coplet[@id='"+copletID+"' and @number='"+element.getAttributeNS(null, "number")+"']", this.xpathProcessor);
                        hasConfig = (customInfo != null);
                    }
                    if (showCustomizePage || !hasConfig ) {
                        Node node = DOMUtil.selectSingleNode(element, "status/customize", this.xpathProcessor);
                        DOMUtil.setValueOfNode(node, "true");
                    } else {
                        Node node = DOMUtil.selectSingleNode(element, "status/customize", this.xpathProcessor);
                        DOMUtil.setValueOfNode(node, "false");
                    }
                } else {
                    Node node = DOMUtil.selectSingleNode(element, "status/customize", this.xpathProcessor);
                    DOMUtil.setValueOfNode(node, "false");
                }

                // Create the parameters for the coplet:
                //   The <status> part is mapped to parameters
                //   id, number and media are added
                SourceParameters p = DOMUtil.createParameters(DOMUtil.getFirstNodeFromPath(element, new String[] {"status"}, false), null);
                p.setSingleParameterValue(PortalConstants.PARAMETER_ID, copletID);
                p.setSingleParameterValue(PortalConstants.PARAMETER_NUMBER, element.getAttributeNS(null, "number"));
                p.setSingleParameterValue(PortalConstants.PARAMETER_MEDIA, media);
                String isPersistent = DOMUtil.getValueOf(copletConf, "configuration/persistent", "false", this.xpathProcessor);
                p.setSingleParameterValue(PortalConstants.PARAMETER_PERSISTENT, isPersistent);

                // the coplet loading is a tricky part:
                // we create an object array containing all information
                // for later processing of the coplet
                // so the processCoplet() method needs no lookup for information
                // again
                loadedCoplet[0] = null;
                loadedCoplet[1] = copletConf;
                loadedCoplet[2] = p;
                loadedCoplet[3] = element;
                loadedCoplet[4] = new Long(System.currentTimeMillis());
                loadedCoplet[5] = new Long(DOMUtil.getValueOf(copletConf, "configuration/timeout", "-1", this.xpathProcessor));
                loadedCoplet[7] = statusProfile;

                CopletThread copletThread = new CopletThread();
                Thread theThread = new Thread(new CocoonRunnable(copletThread));
                loadedCoplet[6] = copletThread;
                copletThread.init(copletID,
                                  ContextHelper.getObjectModel(this.componentContext),
                                  this.getLogger(),
                                  loadedCoplet,
                                  this.manager,
                                  this.resolver,
                                  this.xpathProcessor);
                theThread.start();
                Thread.yield();

                if (!parallelCoplets) {
                    copletThread = (CopletThread)loadedCoplet[6];
                    if (copletThread != null) {
                        long startTime = System.currentTimeMillis() - ((Long)loadedCoplet[4]).longValue();
                        long timeout = ((Long)loadedCoplet[5]).longValue();
                        long waitTime;
                        if (timeout == -1) {
                            waitTime = defaultCopletTimeout;
                        } else {
                            waitTime = timeout - startTime;
                        }

                        while (copletThread != null && waitTime > 2) {
                            try {
                                Thread.sleep(15);
                                waitTime -= 15;
                            } catch(InterruptedException local) {
                                // ignore
                            }
                            copletThread = (CopletThread)loadedCoplet[6];
                        }
                        loadedCoplet[6] = null; // mark as loaded
                    }
                }

            }

        }
    }

    /**
     * Process a coplet which is previously loaded
     */
    private void processCoplet(Object[]    loadedCoplet,
                               XMLConsumer consumer,
                               String      notAvailableMessage,
                               long        defaultCopletTimeout)
    throws ProcessingException,
           SAXException,
           javax.xml.transform.TransformerException  {
        // calling method is synced

        Element copletConf = (Element)loadedCoplet[1];
        Element element    = (Element)loadedCoplet[3];

        String copletID = element.getAttributeNS(null, "id");
        if (copletConf != null) {
            AttributesImpl attr = new AttributesImpl();
            attr.addAttribute("", "id", "id", "CDATA", copletID);
            attr.addAttribute("", "number", "number", "CDATA", element.getAttributeNS(null, "number"));
            attr.addAttribute("", "position", "position", "CDATA", element.getAttributeNS(null, "position"));
            consumer.startElement("", "coplet", "coplet", attr);
            attr.clear();

            // now include all children of the coplet element except status
            NodeList children = copletConf.getChildNodes();
            if (children != null && children.getLength() > 0) {
                int l = children.getLength();
                for(int i = 0; i < l; i++) {
                    if (!children.item(i).getNodeName().equals("status")
                        && children.item(i).getNodeType() == Node.ELEMENT_NODE) {
                        IncludeXMLConsumer.includeNode(children.item(i), consumer, consumer);
                    }
                }
            }

            // now the status parameter
            // SourceParameters p = DOMUtil.createParameters(DOMUtil.getFirstNodeFromPath(element, new String[] {"status"}, false), null);
            consumer.startElement("", "status", "status", attr);
            children = DOMUtil.selectNodeList(element, "status/*", this.xpathProcessor);
            if (children != null && children.getLength() > 0) {
                int l = children.getLength();
                for(int i = 0; i < l; i++) {
                    if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
                        IncludeXMLConsumer.includeNode(children.item(i), consumer, consumer);
                    }
                }
            }
            consumer.endElement("", "status", "status");

            // now the content:
            consumer.startElement("", "content", "content", attr);

            CopletThread thread = (CopletThread)loadedCoplet[6];
            if (thread != null) {
                long startTime = System.currentTimeMillis() - ((Long)loadedCoplet[4]).longValue();
                long timeout = ((Long)loadedCoplet[5]).longValue();
                long waitTime;
                if (timeout == -1) {
                    waitTime = defaultCopletTimeout;
                } else {
                    waitTime = timeout - startTime;
                }

                while (thread != null && waitTime > 2) {
                    try {
                        Thread.sleep(15);
                        waitTime -= 15;
                    } catch(InterruptedException local) {
                        // ignore
                    }
                    thread = (CopletThread)loadedCoplet[6];
                }
            }
            byte[] content = (byte[])loadedCoplet[0];
            if (content != null) {
                if (content.length > 0) {
                    XMLDeserializer interpreter = null;
                    try {
                        interpreter = (XMLDeserializer)this.manager.lookup(XMLDeserializer.ROLE);
                        interpreter.setConsumer(new IncludeXMLConsumer(consumer, consumer));
                        interpreter.deserialize(content);
                    } catch (ComponentException e) {
                        throw new ProcessingException("Component for XMLDeserializer not found." + e, e);
                    } finally {
                        if (interpreter != null) this.manager.release(interpreter);
                    }
                }
            } else {
                notAvailableMessage = DOMUtil.getValueOf(copletConf,
                         "configuration/messages/coplet_not_available", notAvailableMessage, this.xpathProcessor);
                consumer.characters(notAvailableMessage.toCharArray(), 0, notAvailableMessage.length());
            }
            consumer.endElement("", "content", "content");
            consumer.endElement("", "coplet", "coplet");

        }
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#getMediaType()
     */
    public String getMediaType()
    throws ProcessingException {
        this.setup();
        return this.getMediaManager().getMediaType();
    }

    /**
     * Get the coplet with the id
     */
    private Element getCopletConfiguration(String copletID,
                                           Map    defaultCoplets,
                                           Map    mediaCoplets)
    throws ProcessingException {
        // calling method is synced
        String media = this.getMediaManager().getMediaType();
        Map    coplets = (Map)mediaCoplets.get(media);
        Element coplet = null;
        if (coplets != null) coplet = (Element)coplets.get(copletID);
        if (coplet == nullcoplet = (Element)defaultCoplets.get(copletID);
        return coplet;
    }

    /**
     * Get the coplet Element
     */
    private Element getCopletElement(String copletID,
                                     String copletNr,
                                     Node[] miscNodes)
    throws javax.xml.transform.TransformerException {
        // calling method is synced
        Element node = null;

        // first test content, then header and then footer
        int colindex = 8;
        while (node == null && colindex < 13) {
            if (miscNodes[colindex] != null) {
                node = (Element)DOMUtil.getSingleNode(miscNodes[colindex],
                        "coplets/coplet[@id='"+copletID+"' and @number='"+copletNr+"']", this.xpathProcessor);
                colindex++;
            } else {
                colindex = 13;
            }
        }
        if (node == null && miscNodes[PortalConstants.PROFILE_MISC_HEADER_CONTENT_NODE] != null) {
            node = (Element)DOMUtil.getSingleNode(miscNodes[PortalConstants.PROFILE_MISC_HEADER_CONTENT_NODE],
                      "coplet[@id='"+copletID+"' and @number='"+copletNr+"']", this.xpathProcessor);
        }
        if (node == null && miscNodes[PortalConstants.PROFILE_MISC_FOOTER_CONTENT_NODE] != null) {
            node = (Element)DOMUtil.getSingleNode(miscNodes[PortalConstants.PROFILE_MISC_FOOTER_CONTENT_NODE],
                      "coplet[@id='"+copletID+"' and @number='"+copletNr+"']", this.xpathProcessor);
        }
        return node;
    }

    /**
     * Modify the coplet.
     * This method returns true if the type informations must be recalculated
     */
    private boolean modifyCoplet(String requestString,
                                 SessionContext context,
                                 Map             theProfile,
                                 DocumentFragment profile)
    throws ProcessingException, javax.xml.transform.TransformerException {
        // synchronized as the caller is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN modifyCoplet request=" + requestString);
        }
        boolean result = false;


        int pos, pos2;
        pos  = requestString.indexOf('_');
        pos2 = requestString.indexOf('_', pos+1);
        if (pos != -1 && pos2 != -1) {
            Element coplet = null;

            String copletID;
            String copletNr;
            String argument = null;

            copletID = requestString.substring(pos+1,pos2);
            copletNr = requestString.substring(pos2+1);
            pos = copletNr.indexOf('_');
            if (pos != -1) {
                argument = copletNr.substring(pos+1);
                copletNr = copletNr.substring(0, pos);
            }

            // create a new coplet: in the given column, header or footer
            if (requestString.startsWith(PortalManagerImpl.REQ_CMD_NEW)
                && this.isCopletAvailable(context, copletID,
                                 (Map)theProfile.get(PortalConstants.PROFILE_DEFAULT_COPLETS),
                                 (Map)theProfile.get(PortalConstants.PROFILE_MEDIA_COPLETS))) {
                Node[] miscNodes = (Node[])theProfile.get(PortalConstants.PROFILE_MISC_POINTER);
                // determine the coplet number
                Node   lastCoplet = miscNodes[PortalConstants.PROFILE_MISC_LAST_COPLET_NODE];
                String lastNumber = null;
                if (lastCoplet != null) {
                    lastNumber = ((Element)lastCoplet).getAttributeNS(null, "number");
                    if (lastNumber != null) {
                        int value = new Integer(lastNumber).intValue();
                        value++;
                        lastNumber = ""+value;
                    }
                }
                if (lastNumber == null) lastNumber = "0";

                Node copletsNode;
                if (copletNr.equals("header")) {
                    copletsNode = miscNodes[PortalConstants.PROFILE_MISC_HEADER_CONTENT_NODE];
                    if (copletsNode == null) {
                        copletsNode = DOMUtil.selectSingleNode(profile, "profile/portal-profile/content/header", this.xpathProcessor);
                        miscNodes[PortalConstants.PROFILE_MISC_HEADER_CONTENT_NODE] = copletsNode;
                    } else { // remove old coplet
                        Node oldCoplet = DOMUtil.getFirstNodeFromPath(copletsNode, new String[] {"coplet"}, false);
                        if (oldCoplet != null) copletsNode.removeChild(oldCoplet);
                    }
                } else if (copletNr.equals("footer")) {
                    copletsNode = miscNodes[PortalConstants.PROFILE_MISC_FOOTER_CONTENT_NODE];
                    if (copletsNode == null) {
                        copletsNode = DOMUtil.selectSingleNode(profile, "profile/portal-profile/content/footer", this.xpathProcessor);
                        miscNodes[PortalConstants.PROFILE_MISC_FOOTER_CONTENT_NODE] = copletsNode;
                    } else { // remove old coplet
                        Node oldCoplet = DOMUtil.getFirstNodeFromPath(copletsNode, new String[] {"coplet"}, false);
                        if (oldCoplet != null) copletsNode.removeChild(oldCoplet);
                    }
                } else {
                    Node columnNode = miscNodes[7+new Integer(copletNr).intValue()];
                    copletsNode = DOMUtil.getFirstNodeFromPath(columnNode, new String[] {"coplets"}, false);
                }
                Element copletNode;
                Document doc = copletsNode.getOwnerDocument();
                copletNode = doc.createElementNS(null, "coplet");
                copletsNode.appendChild(copletNode);
                copletNode.setAttributeNS(null, "id", copletID);
                copletNode.setAttributeNS(null, "number", lastNumber);
                // set position
                NodeList childs = DOMUtil.getNodeListFromPath(copletsNode, new String[] {"coplet"});
                int childsCount = (childs == null ? 0 : childs.getLength());
                copletNode.setAttributeNS(null, "position", ""+(childsCount));
                miscNodes[PortalConstants.PROFILE_MISC_LAST_COPLET_NODE] = copletNode;

                // copy status
                Element configElement = this.getCopletConfiguration(copletID,
                                                      (Map)theProfile.get(PortalConstants.PROFILE_DEFAULT_COPLETS),
                                                      (Map)theProfile.get(PortalConstants.PROFILE_MEDIA_COPLETS));
                Element configStatus = (Element)DOMUtil.getFirstNodeFromPath(configElement, new String[] {"status"}, false);
                copletNode.appendChild(configStatus.cloneNode(true));

                // clear type information for each status
                Element status = (Element)copletNode.getElementsByTagName("status").item(0);
                NodeList parameters = status.getChildNodes();
                Node    current;
                Element statusNode;
                if (parameters != null) {
                    for(int i = 0; i < parameters.getLength(); i++) {
                        current = parameters.item(i);
                        if (current.getNodeType() == Node.ELEMENT_NODE) {
                            statusNode = (Element)current;
                            if (statusNode.hasAttributeNS(null, "formpath"))
                                statusNode.removeAttributeNS(null, "formpath");
                            if (statusNode.hasAttributeNS(null, "formtype"))
                                statusNode.removeAttributeNS(null, "formtype");
                            if (statusNode.hasAttributeNS(null, "formdescription"))
                                statusNode.removeAttributeNS(null, "formdescription");
                        }
                    }
                }
                result = true;

           } else {
                coplet = this.getCopletElement(copletID,
                                    copletNr,
                                    (Node[])theProfile.get(PortalConstants.PROFILE_MISC_POINTER));
                if (coplet != null) {
                    if (requestString.startsWith(PortalManagerImpl.REQ_CMD_CLOSE) ||
                        requestString.startsWith(PortalManagerImpl.REQ_CMD_HIDE)) {
                         Node node = DOMUtil.selectSingleNode(coplet, "status/visible", this.xpathProcessor);
                         DOMUtil.setValueOfNode(node, "false");
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_OPEN) ||
                        requestString.startsWith(PortalManagerImpl.REQ_CMD_SHOW)) {
                         Node node = DOMUtil.selectSingleNode(coplet, "status/visible", this.xpathProcessor);
                         DOMUtil.setValueOfNode(node, "true");
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_MINIMIZE)) {
                         Node node = DOMUtil.selectSingleNode(coplet, "status/size", this.xpathProcessor);
                         DOMUtil.setValueOfNode(node, "min");
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_MAXIMIZE)) {
                         Node node = DOMUtil.selectSingleNode(coplet, "status/size", this.xpathProcessor);
                         DOMUtil.setValueOfNode(node, "max");
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_CUSTOMIZE)) {
                         Node node = DOMUtil.selectSingleNode(coplet, "status/customize", this.xpathProcessor);
                         DOMUtil.setValueOfNode(node, "true");
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_UPDATE)) {
                         Node node = DOMUtil.selectSingleNode(coplet, "status/customize", this.xpathProcessor);
                         DOMUtil.setValueOfNode(node, "false");
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_DELETE)) {
                        // delete the status of the coplet
                        Node statusNode = DOMUtil.getSingleNode(profile,
                             "profile/status-profile/customization/coplet[@id='"+copletID+"' and @number='"+copletNr+"']", this.xpathProcessor);
                        if (statusNode != null) {
                            statusNode.getParentNode().removeChild(statusNode);
                            Element configElement = this.getCopletConfiguration(copletID,
                                                      (Map)theProfile.get(PortalConstants.PROFILE_DEFAULT_COPLETS),
                                                      (Map)theProfile.get(PortalConstants.PROFILE_MEDIA_COPLETS));
                            boolean isPersistent = DOMUtil.getValueAsBooleanOf(configElement, "configuration/persistent", false, this.xpathProcessor);
                            if (isPersistent) {
                                // mark the status profile to be saved
                                theProfile.put(PortalConstants.PROFILE_SAVE_STATUS_FLAG, "true");
                            }
                        }
                        String posAttr = coplet.getAttributeNS(null, "position");
                        NodeList followUps = DOMUtil.selectNodeList(coplet.getParentNode(), "coplet[@position > '"+posAttr+"']", this.xpathProcessor);
                        coplet.getParentNode().removeChild(coplet);
                        coplet = null;
                        if (followUps != null) {
                            int value;
                            for(int i = 0; i < followUps.getLength(); i++) {
                                value = new Integer(((Element)followUps.item(i)).getAttributeNS(null, "position")).intValue();
                                value -= 1;
                                ((Element)followUps.item(i)).setAttributeNS(null, "position", "" + value);
                           }
                        }
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_MOVE)) {
                        if (argument != null) {
                            Element  copletsElement = (Element)DOMUtil.getSingleNode(profile,
                                  "profile/portal-profile/content/column[@position='"+argument+"']/coplets", this.xpathProcessor);
                            if (copletsElement != null) {
                                if (!coplet.getParentNode().equals(copletsElement)) {
                                     String posAttr = coplet.getAttributeNS(null, "position");
                                     NodeList followUps = DOMUtil.selectNodeList(coplet.getParentNode(), "coplet[@position > '"+posAttr+"']", this.xpathProcessor);
                                     coplet.getParentNode().removeChild(coplet);
                                     // set position attribute
                                     NodeList childs = DOMUtil.getNodeListFromPath(copletsElement, new String[] {"coplet"});
                                     int childsCount = (childs == null ? 0 : childs.getLength());
                                     coplet.setAttributeNS(null, "position", "" + (childsCount + 1));
                                     copletsElement.appendChild(coplet);
                                     if (followUps != null) {
                                         int value;
                                         for(int i = 0; i < followUps.getLength(); i++) {
                                             value = new Integer(((Element)followUps.item(i)).getAttributeNS(null, "position")).intValue();
                                             value -= 1;
                                             ((Element)followUps.item(i)).setAttributeNS(null, "position", "" + value);
                                         }
                                     }
                                 }
                            }
                        }
                    } else if (requestString.startsWith(PortalManagerImpl.REQ_CMD_MOVEROW)) {
                        if (argument != null) {
                            Element newCoplet = (Element)DOMUtil.getSingleNode(coplet.getParentNode(),
                                                 "coplet[@position='"+argument+"']", this.xpathProcessor);
                            if (newCoplet != null) {
                                String position = coplet.getAttributeNS(null, "position");
                                coplet.removeAttributeNS(null, "position");
                                coplet.setAttributeNS(null, "position", argument);
                                newCoplet.removeAttributeNS(null, "position");
                                newCoplet.setAttributeNS(null, "position", position);
                            }
                        }
                    }
                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END modifyCoplet calculate="+result);
        }
        return result;
    }

    /**
     * Check if the coplet is available for the current logged in user
     * If the user is not logged in, this returns false.
     * First the default coplets are searched. If none is found then
     * the coplets for each media are searched.
     */
    private boolean isCopletAvailable(SessionContext context,
                                    String copletID,
                                    Map defaultCoplets,
                                    Map mediaCoplets)
    {
        // no sync required
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN isCopletAvailable coplet="+copletID);
        }
        boolean result = false;

        if (context != null) {
            result = defaultCoplets.containsKey(copletID);
            if (!result) {
                Iterator iter = mediaCoplets.values().iterator();
                while (!result && iter.hasNext()) {
                    result = ((Map)iter.next()).containsKey(copletID);
                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END isCopletAvailable result=" + result);
        }
        return result;
    }

    /* (non-Javadoc)
     * @see org.apache.cocoon.webapps.portal.components.PortalManager#checkAuthentication(org.apache.cocoon.environment.Redirector, java.lang.String)
     */
    public boolean checkAuthentication(Redirector redirector, String copletID)
    throws SAXException, IOException, ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN checkAuthentication coplet="+copletID);
        }
        this.setup();
        boolean result = false;
        SessionContext context = this.getContext(false);
        if (context != null
            && context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE) != null) {

            try {
                this.getTransactionManager().startReadingTransaction(context);
                Map theProfile = this.retrieveProfile(this.getProfileID(PortalManagerImpl.BUILDTYPE_VALUE_ID,
                     (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ROLE),
                     (String)context.getAttribute(PortalManagerImpl.ATTRIBUTE_PORTAL_ID), false));

                if (theProfile != null) {
                    if (copletID == null || copletID.trim().length() == 0) {
                        result = true;
                    } else {
                        result = this.isCopletAvailable(context,
                                      copletID,
                                      (Map)theProfile.get(PortalConstants.PROFILE_DEFAULT_COPLETS),
                                      (Map)theProfile.get(PortalConstants.PROFILE_MEDIA_COPLETS));
                    }
                }
            } finally {
                this.getTransactionManager().stopReadingTransaction(context);
            } // end synced
        }


        if (!result) {
            Map config = this.getConfiguration();
            if (config != null) {
                String redirectURI = (String)config.get(PortalConstants.CONF_AUTH_REDIRECT);
                if (redirectURI == null) {
                    redirectURI = (String)config.get(PortalConstants.CONF_PORTAL_URI);
                }
                if (redirectURI != null) {
                    redirector.globalRedirect( false, redirectURI );
                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END checkAuthentication result=" + result);
        }
        return result;
    }

    /**
     * Get the configuration. This configuration is an authentication application configuration
     * for the current application with the name "portal".
     * The first time this configuration is build it is stored in the session
     * so later requests get the cached result.
     */
    private Map getConfiguration()
    throws ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN getConfiguration");
        }
        Map result = null;
        RequestState reqstate = this.getRequestState();
        String appName = reqstate.getApplicationName();
        String handlerName = reqstate.getHandlerName();
        Session session = this.getSessionManager().getSession(false);
        if (session != null && appName != null && handlerName != null) {

            synchronized (session) {
                result = (Map)session.getAttribute(PortalConstants.ATTRIBUTE_CONFIGURATION + handlerName + ':' + appName);
                if (result == null) {

                    try {
                        Configuration config;

                        Configuration conf = reqstate.getModuleConfiguration(PortalConstants.AUTHENTICATION_MODULE_NAME);
                        if (conf == null) {
                            throw new ProcessingException("portal: Configuration for application '" + appName + "' not found.");
                        }
                        result = new HashMap(10, 2);
                        // auth-redirect (optional)
                        config = conf.getChild("auth-redirect", false);
                        if (config != null) {
                            result.put(PortalConstants.CONF_AUTH_REDIRECT, config.getValue());
                        }

                        // portal-uri (required)
                        config = conf.getChild("portal-uri", false);
                        if (config == null) {
                            throw new ProcessingException("portal: portal-uri required for application '"+appName+"'");
                        }
                        result.put(PortalConstants.CONF_PORTAL_URI, config.getValue());

                        // profile-cache (optional)
                        config = conf.getChild("profile-cache", false);
                        if (config != null && config.getValueAsBoolean()) {
                            result.put(PortalConstants.CONF_PROFILE_CACHE, appName);
                        }

                        // parallel coplets
                        config = conf.getChild("process-coplets-parallel", false);
                        if (config != null) {
                            result.put(PortalConstants.CONF_PARALLEL_COPLETS, new Boolean(config.getValueAsBoolean(false)));
                        } else {
                            result.put(PortalConstants.CONF_PARALLEL_COPLETS, Boolean.FALSE);
                        }

                        // timeout
                        config = conf.getChild("default-coplet-timeout", false);
                        if (config != null) {
                            result.put(PortalConstants.CONF_COPLET_TIMEOUT, new Long(config.getValueAsLong(600000)));
                        } else {
                            result.put(PortalConstants.CONF_COPLET_TIMEOUT, new Long(600000));
                        }

                        // and now the profile
                        config = conf.getChild("profile", false);
                        if (config == null) throw new ProcessingException("portal: profile configuration required for application '" + appName + "'");
                        Configuration child;

                        // build resource (optional)
                        child = config.getChild("buildprofile", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_BUILD_RESOURCE, child.getAttribute("uri"));
                        }

                        // base resource, type is optional
                        child = config.getChild("layout-base", false);
                        if (child == null) {
                            throw new ProcessingException("portal: layout-base required for application '" + appName + "'");
                        }
                        result.put(PortalConstants.CONF_LAYOUTBASE_RESOURCE, child.getAttribute("uri"));
                        child = config.getChild("coplet-base", false);
                        if (child == null) {
                            throw new ProcessingException("portal: coplet-base required for application '" + appName + "'");
                        }
                        result.put(PortalConstants.CONF_COPLETBASE_RESOURCE, child.getAttribute("uri"));
                        child = config.getChild("type-base", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_TYPEBASE_RESOURCE, child.getAttribute("uri"));
                        }

                        // coplet base save (is optional)
                        child = config.getChild("coplet-base-save", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_COPLETBASE_SAVE_RESOURCE, child.getAttribute("uri"));
                        }

                        // global delta (load required)
                        child = config.getChild("global-delta-load", false);
                        if (child == null) {
                            throw new ProcessingException("portal: global-delta-load required for application '" + appName + "'");
                        }
                        result.put(PortalConstants.CONF_GLOBALDELTA_LOADRESOURCE, child.getAttribute("uri"));
                        child = config.getChild("global-delta-save", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_GLOBALDELTA_SAVERESOURCE, child.getAttribute("uri"));
                        }
                        child = config.getChild("global-type-delta", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_GLOBALDELTA_TYPERESOURCE, child.getAttribute("uri"));
                        }

                        // role delta (optional)
                        child = config.getChild("role-delta-load", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_ROLEDELTA_LOADRESOURCE, child.getAttribute("uri"));
                        }
                        child = config.getChild("role-delta-save", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_ROLEDELTA_SAVERESOURCE, child.getAttribute("uri"));
                        }
                        child = config.getChild("role-type-delta", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_ROLEDELTA_TYPERESOURCE, child.getAttribute("uri"));
                        }

                        // User delta
                        child = config.getChild("user-delta-load", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_USERDELTA_LOADRESOURCE, child.getAttribute("uri"));
                        }
                        child = config.getChild("user-delta-save", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_USERDELTA_SAVERESOURCE, child.getAttribute("uri"));
                        }
                        child = config.getChild("user-type-delta", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_USERDELTA_TYPERESOURCE, child.getAttribute("uri"));
                        }

                        // Personal information
                        child = config.getChild("user-status-load", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_STATUS_LOADRESOURCE, child.getAttribute("uri"));
                        }
                        child = config.getChild("user-status-save", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_STATUS_SAVERESOURCE, child.getAttribute("uri"));
                        }

                        // Admin Type profil
                        child = config.getChild("admin-type-base", false);
                        if (child != null) {
                            result.put(PortalConstants.CONF_ADMIN_TYPE_BASE, child.getAttribute("uri"));
                        }

                        // store the config in the session
                        session.setAttribute(PortalConstants.ATTRIBUTE_CONFIGURATION + handlerName + ':' + appName, result);
                    } catch (ConfigurationException conf) {
                        throw new ProcessingException("ConfigurationException: " + conf, conf);
                    }
                }
            }

        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END getConfiguration conf="+result);
        }
        return result;
    }

    /**
     * Build the profile for the required level if not already done
     */
    private void createProfile(SessionContext context,
                              String type,
                              String role,
                              String id,
                              boolean adminProfile)
    throws SAXException, IOException, ProcessingException {
        // no sync required
        if (this.getLogger().isDebugEnabled() ) {
            this.getLogger().debug("BEGIN createProfile context="+context+
                                   ", type="+type+
                                   ", role="+role+
                                   ", id="+id);
        }

        RequestState reqstate = this.getRequestState();
        SourceParameters pars = reqstate.getHandler().getContext().getContextInfoAsParameters();
        pars.setSingleParameterValue("type", type);
        pars.setSingleParameterValue("admin", (adminProfile ? "true" : "false"));

        if (!type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID) || role != null) {
            pars.setSingleParameterValue("ID", id);
            pars.setSingleParameterValue("role", role);
        } else {
            id = pars.getParameter("ID", null);
            role = pars.getParameter("role", null);
        }

        Map map = this.getConfiguration();
        if (map == null) {
            throw new ProcessingException("portal Configuration not found.");
        }

        // is the configuration build by using a own resource?
        String resource = (String)map.get(PortalConstants.CONF_BUILD_RESOURCE);
        if (resource != null) {
            if (this.getLogger().isInfoEnabled()) {
                this.getLogger().info("Building portal profile: " + resource);
            }
            SourceUtil.readDOM(resource,
                               null,
                               pars,
                               this.resolver);
        } else {
            this.buildProfile(type, role, id, adminProfile);
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END createProfile");
        }

    }

    /**
     * Get the base profile for the current application.
     * The base profile consists of the layout and the coplet profile
     * and optional the type profile
     */
    private DocumentFragment buildBaseProfile(Map config, boolean adminProfile)
    throws ProcessingException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN buildBaseProfile config="+config+", adminProfile="+adminProfile);
        }
        DocumentFragment copletsFragment;
        DocumentFragment layoutFragment;
        DocumentFragment typeFragment;
        DocumentFragment profile;
        Document         profileDoc;
        Element          profileRoot;
        String           res;

        SourceParameters pars = new SourceParameters();
        RequestState reqstate = this.getRequestState();
        pars.setSingleParameterValue("application", reqstate.getApplicationName());
        pars.setSingleParameterValue("handler", reqstate.getHandlerName());
        pars.setSingleParameterValue("profile", "coplet-base");

        // First load the base profiles: copletProfile + layoutProfile
        res = (String)config.get(PortalConstants.CONF_COPLETBASE_RESOURCE);
        if (res == null) {
            throw new ProcessingException("No configuration for portal-coplet base profile found.");
        }
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("Loading coplet base profile");
        }
        copletsFragment = SourceUtil.readDOM(res,
                               null,
                               pars,
                               this.resolver);

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("coplet base profile loaded");
        }
        res = (String)config.get(PortalConstants.CONF_LAYOUTBASE_RESOURCE);
        if (res == null) {
            throw new ProcessingException("No configuration for portal-layout base profile found.");
        }
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("loading layout base profile");
        }
        pars.setSingleParameterValue("profile", "layout-base");
        layoutFragment = SourceUtil.readDOM(res,
                               null,
                               pars,
                               this.resolver);

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("layout base profile loaded");
        }
        // now create the base profile containing the above profiles
        profileDoc = DOMUtil.createDocument();
        profile = profileDoc.createDocumentFragment();
        profileRoot = profileDoc.createElementNS(null, "profile");
        profile.appendChild(profileRoot);
        profileRoot.appendChild(profileDoc.importNode(DOMUtil.selectSingleNode(layoutFragment,
                                                                  "layout-profile", this.xpathProcessor), true));
        profileRoot.appendChild(profileDoc.importNode(DOMUtil.selectSingleNode(copletsFragment,
                                                                  "coplets-profile", this.xpathProcessor), true));

        // if avalailable append the type profile
        if (adminProfile) {
            res = (String)config.get(PortalConstants.CONF_ADMIN_TYPE_BASE);
            pars.setSingleParameterValue("profile", "admin-type-base");
        } else {
            res = (String)config.get(PortalConstants.CONF_TYPEBASE_RESOURCE);
            pars.setSingleParameterValue("profile", "type-base");
        }
        if (res != null) {
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("loading type base profile");
            }
            typeFragment = SourceUtil.readDOM(res,
                                   null,
                                   pars,
                                   this.resolver);
            profileRoot.appendChild(profileDoc.importNode(DOMUtil.selectSingleNode(typeFragment,
                              "type-profile", this.xpathProcessor), true));

            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("type base profile loaded");
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildBaseProfile profile=" + profile);
        }
        return profile;
    }

    /**
     * Build the global profile.
     */
    private void buildGlobalProfile(Element profileRoot,
                                    Map config,
                                    boolean adminProfile)
    throws ProcessingException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN buildGlobalProfile profileRoot="+profileRoot+", config="+config+", adminProfile="+adminProfile);
        }
        DocumentFragment globalFragment;
        String res = (String)config.get(PortalConstants.CONF_GLOBALDELTA_LOADRESOURCE);
        if (res == null) {
            throw new ProcessingException("No configuration for portal-role delta profile found.");
        }
        SourceParameters pars = new SourceParameters();
        RequestState reqstate = this.getRequestState();
        pars.setSingleParameterValue("application", reqstate.getApplicationName());
        pars.setSingleParameterValue("handler", reqstate.getHandlerName());
        pars.setSingleParameterValue("profile", "global-delta");

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("loading global profile");
        }
        globalFragment = SourceUtil.readDOM(res,
                                   null,
                                   pars,
                                   this.resolver);
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("global profile loaded");
        }
        this.importProfileDelta(profileRoot, globalFragment, "global-delta", "layout-delta");
        this.importProfileDelta(profileRoot, globalFragment, "global-delta", "coplets-delta");
        this.addProfilePart(profileRoot, globalFragment, "global-delta", "portal-profile");
        this.addProfilePart(profileRoot, globalFragment, "global-delta", "personal-profile");

        // types
        res = (String)config.get(PortalConstants.CONF_GLOBALDELTA_TYPERESOURCE);
        if (!adminProfile && res != null) {
            pars.setSingleParameterValue("profile", "global-type-delta");
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("loading global type profile");
            }
            globalFragment = SourceUtil.readDOM(res,
                                       null,
                                       pars,
                                       this.resolver);
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("global type profile loaded");
            }
            this.addProfilePart(profileRoot, globalFragment, "global-delta", "type-profile");
        }
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END buildGlobalProfile");
        }
    }

    /**
     * Build the role profile
     */
    private void buildRoleProfile(Element profileRoot,
                                            Map config,
                                            String role,
                                            boolean adminProfile)
    throws ProcessingException {
        // calling method is synced

        DocumentFragment roleFragment;
        RequestState reqstate = this.getRequestState();
        SourceParameters pars;
        pars = new SourceParameters();
        pars.setSingleParameterValue("role", role);
        pars.setSingleParameterValue("application", reqstate.getApplicationName());
        pars.setSingleParameterValue("handler", reqstate.getHandlerName());
        pars.setSingleParameterValue("profile", "role-delta");

        String res = (String)config.get(PortalConstants.CONF_ROLEDELTA_LOADRESOURCE);
        if (res != null) {
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("loading role profile");
            }
            roleFragment = SourceUtil.readDOM(res,
                                       null,
                                       pars,
                                       this.resolver);
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("role profile loaded");
            }
            this.importProfileDelta(profileRoot, roleFragment, "role-delta", "layout-delta");
            this.importProfileDelta(profileRoot, roleFragment, "role-delta", "coplets-delta");
            this.addProfilePart(profileRoot, roleFragment, "role-delta", "portal-profile");
            this.importProfileDelta(profileRoot, roleFragment, "role-delta", "personal-delta");
        }

        // types
        res = (String)config.get(PortalConstants.CONF_ROLEDELTA_TYPERESOURCE);
        if (!adminProfile && res != null) {
            pars.setSingleParameterValue("profile", "role-type-delta");
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("loading role type profile");
            }
            roleFragment = SourceUtil.readDOM(res,
                                   null,
                                   pars,
                                   this.resolver);
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("role type profile loaded");
            }
            this.addProfilePart(profileRoot, roleFragment, "role-delta", "type-profile");
        }
    }

    /**
     * Build the user profile
     */
    private void buildUserProfile(Element profileRoot,
                                Map config,
                                String role,
                                String id,
                                boolean adminProfile)
    throws ProcessingException {
        // calling method is synced
        DocumentFragment userFragment;
        RequestState reqstate = this.getRequestState();
        SourceParameters pars;
        pars = new SourceParameters();
        pars.setSingleParameterValue("ID", id);
        pars.setSingleParameterValue("role", role);
        pars.setSingleParameterValue("application", reqstate.getApplicationName());
        pars.setSingleParameterValue("handler", reqstate.getHandlerName());
        pars.setSingleParameterValue("profile", "user-delta");

        String res = (String)config.get(PortalConstants.CONF_USERDELTA_LOADRESOURCE);
        if (res != null) {
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("loading user profile");
            }
            userFragment = SourceUtil.readDOM(res,
                                   null,
                                   pars,
                                   this.resolver);
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("user profile loaded");
            }
            this.importProfileDelta(profileRoot, userFragment, "user-delta", "layout-delta");
            this.importProfileDelta(profileRoot, userFragment, "user-delta", "coplets-delta");
            this.addProfilePart(profileRoot, userFragment, "user-delta", "portal-profile");
            this.importProfileDelta(profileRoot, userFragment, "user-delta", "personal-delta");
        }

        // types
        res = (String)config.get(PortalConstants.CONF_USERDELTA_TYPERESOURCE);
        if (!adminProfile && res != null) {
            pars.setSingleParameterValue("profile", "user-type-delta");
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("loading user type profile");
            }
            userFragment = SourceUtil.readDOM(res,
                                   null,
                                   pars,
                                   this.resolver);
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("user type profile loaded");
            }
            this.addProfilePart(profileRoot, userFragment, "user-delta", "type-profile");
        }
    }

    /**
     * Load the user status profile (if available)
     */
    private void buildUserStatusProfile(Element profileRoot,
                                        Map config,
                                        String role,
                                        String id)
    throws ProcessingException {
        // calling method is synced
        String res = (String)config.get(PortalConstants.CONF_STATUS_LOADRESOURCE);

        // remove the old status profile
        Node statusProfile = DOMUtil.getFirstNodeFromPath(profileRoot, new String[] {"status-profile"}, false);
        if (statusProfile != null) {
            profileRoot.removeChild(statusProfile);
        }

        if (res != null) {
            DocumentFragment userFragment;
            SourceParameters pars;
            RequestState reqstate = this.getRequestState();
            pars = new SourceParameters();
            pars.setSingleParameterValue("ID", id);
            pars.setSingleParameterValue("role", role);
            pars.setSingleParameterValue("application", reqstate.getApplicationName());
            pars.setSingleParameterValue("handler", reqstate.getHandlerName());
            pars.setSingleParameterValue("profile", "user-status");
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("loading user status profile");
            }
            userFragment = SourceUtil.readDOM(res,
                                   null,
                                   pars,
                                   this.resolver);
            if (this.getLogger().isDebugEnabled()) {
                this.getLogger().debug("user status profile loaded");
            }
            this.addProfilePart(profileRoot, userFragment, null, "status-profile");
        }
        // test if the status-profile node is available
        // if not create one
        if (DOMUtil.getFirstNodeFromPath(profileRoot, new String[] {"status-profile"}, false) == null) {
            statusProfile = profileRoot.getOwnerDocument().createElementNS(null, "status-profile");
            profileRoot.appendChild(statusProfile);
        }
    }

    /**
     * Save the user status profile (if available)
     */
    private void saveUserStatusProfile(Map profile,
                                       Map config,
                                       String role,
                                       String id)
    throws ProcessingException {
        // calling method is synced
        String res = (String)config.get(PortalConstants.CONF_STATUS_SAVERESOURCE);
        Element statusProfile = (Element)DOMUtil.getFirstNodeFromPath((DocumentFragment)profile.get(PortalConstants.PROFILE_PROFILE),
                         new String[] {"profile","status-profile"}, false);
        if (res != null && statusProfile != null) {
            DocumentFragment userFragment = statusProfile.getOwnerDocument().createDocumentFragment();
            Element saveStatus = (Element)statusProfile.cloneNode(true);
            userFragment.appendChild(saveStatus);
            // now filter all not persistent coplets!
            NodeList list = DOMUtil.getNodeListFromPath(saveStatus, new String[] {"customization","coplet"});
            String copletID;
            Element coplet;
            Element copletConfig;
            Map copletConfigs = (Map)profile.get(PortalConstants.PROFILE_DEFAULT_COPLETS);
            Map mediaCopletConfigs = (Map)profile.get(PortalConstants.PROFILE_MEDIA_COPLETS);
            boolean isPersistent;
            for(int i = 0; i < list.getLength(); i++) {
                coplet = (Element)list.item(i);
                copletID = coplet.getAttributeNS(null, "id");
                copletConfig = this.getCopletConfiguration(copletID, copletConfigs, mediaCopletConfigs);
                isPersistent = DOMUtil.getValueAsBooleanOf(copletConfig, "configuration/persistent", false, this.xpathProcessor);
                if (!isPersistent) {
                    coplet.getParentNode().removeChild(coplet);
                }
            }

            try {

                RequestState reqstate = this.getRequestState();
                SourceParameters pars;
                pars = new SourceParameters();
                pars.setSingleParameterValue("ID", id);
                pars.setSingleParameterValue("role", role);
                pars.setSingleParameterValue("application", reqstate.getApplicationName());
                pars.setSingleParameterValue("handler", reqstate.getHandlerName());
                pars.setSingleParameterValue("profile", "user-status");

                SourceUtil.writeDOM(res,
                                   null,
                                   pars,
                                   userFragment,
                                   this.resolver,
                                   "xml");

            } finally {
                userFragment.removeChild(saveStatus);
            }
        }
    }

    /**
     * Change the profile according to the request parameter
     */
    private void changeProfile()
    throws ProcessingException, SAXException, IOException {
        // synchronized
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN changeProfile");
        }
        Request request = ContextHelper.getRequest(this.componentContext);
        SessionContext context = this.getContext(false);

        if (context != null) {
            try {
                Map theProfile = null;
                String profileID = request.getParameter(PortalManagerImpl.REQ_PARAMETER_PROFILE);
                if (profileID != null) {
                    theProfile = this.retrieveProfile(profileID);
                }

                if (theProfile != null) {
                    synchronized (context) {
                        DocumentFragment profile = (DocumentFragment)theProfile.get(PortalConstants.PROFILE_PROFILE);
                        Node[]           miscNodes = (Node[])theProfile.get(PortalConstants.PROFILE_MISC_POINTER);
                        Element          columns = (Element)miscNodes[PortalConstants.PROFILE_MISC_COLUMNS_NODE];
                        Enumeration      enumeration = request.getParameterNames();
                        String           current;
                        boolean          saveProfile = false;

                        // first iteration: all changing commands
                        while (enumeration.hasMoreElements()) {
                            current = (String)enumeration.nextElement();
                            if (current.startsWith(PortalManagerImpl.REQ_PARAMETER_CONF)) {
                                int pos1, pos2;
                                pos1 = current.indexOf('.');
                                pos2 = current.indexOf('.', pos1+1);
                                if (pos1 != -1 && pos2 != -1) {
                                    int pathIndex = new Integer(current.substring(pos1+1, pos2)).intValue();
                                    int place= new Integer(current.substring(pos2+1)).intValue();
                                    List typePaths = (List)theProfile.get(PortalConstants.PROFILE_TYPE_CONF_PATHS);
                                    String path = (String)typePaths.get(pathIndex);
                                    if (path != null) {
                                        NodeList nodes = DOMUtil.selectNodeList(profile, path, this.xpathProcessor);
                                        if (nodes != null) {
                                            Node node = nodes.item(place);
                                            if (node != null) {
                                                if (!node.equals(columns)) {
                                                        DOMUtil.setValueOfNode(node, request.getParameter(current));
                                                }
                                            }
                                        }

                                    }
                                }
                            }
                        }

                        // second: all new
                        boolean     calculate = false;
                        enumeration = request.getParameterNames();
                        while (enumeration.hasMoreElements()) {

                            current = (String)enumeration.nextElement();
                            if (current.startsWith(PortalManagerImpl.REQ_PARAMETER_CONF)) {
                                int pos1, pos2;
                                pos1 = current.indexOf('.');
                                pos2 = current.indexOf('.', pos1+1);
                                if (pos1 != -1 && pos2 != -1) {
                                    int pathIndex = new Integer(current.substring(pos1+1, pos2)).intValue();
                                    int place= new Integer(current.substring(pos2+1)).intValue();
                                    List typePaths = (List)theProfile.get(PortalConstants.PROFILE_TYPE_CONF_PATHS);
                                    String path = (String)typePaths.get(pathIndex);
                                    if (path != null) {
                                        NodeList nodes = DOMUtil.selectNodeList(profile, path, this.xpathProcessor);
                                        if (nodes != null) {
                                            Node node = nodes.item(place);
                                            if (node != null) {
                                                if (node.equals(columns)) {
                                                    int columnNumber = new Integer(request.getParameter(current)).intValue();
                                                    int oldNumber = new Integer(DOMUtil.getValueOfNode(columns)).intValue();
                                                    if (columnNumber > 0 && columnNumber != oldNumber && columnNumber <= PortalConstants.MAX_COLUMNS) {
                                                        this.changeColumns(profile,
                                                               oldNumber,
                                                               columnNumber,
                                                               miscNodes);
                                                        calculate = true;
                                                        DOMUtil.setValueOfNode(node, request.getParameter(current));
                                                    }
                                                }
                                            }
                                        }

                                    }
                                }

                            } else if (current.equals(PortalManagerImpl.REQ_PARAMETER_CMD)) {
                                String[] cmds = request.getParameterValues(current);
                                if (cmds != null && cmds.length > 0) {
                                    for(int i = 0; i < cmds.length; i++) {
                                        if (cmds[i].equals(PortalManagerImpl.REQ_CMD_SAVEPROFILE)) {
                                            saveProfile = true;
                                        } else {
                                            if (this.modifyCoplet(cmds[i], context, theProfile, profile)) {
                                                calculate = true;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        // set type infos
                        if (calculate) {
                            this.setTypeInfo(profile,
                                 (List)theProfile.get(PortalConstants.PROFILE_TYPE_PATHS),
                                 (List)theProfile.get(PortalConstants.PROFILE_TYPE_CONF_PATHS));
                        }

                        // test if the status profile changed
                        Object statusChanged = theProfile.get(PortalConstants.PROFILE_SAVE_STATUS_FLAG);
                        if (statusChanged != null) {
                            theProfile.remove(PortalConstants.PROFILE_SAVE_STATUS_FLAG);
                            this.saveUserStatusProfile(theProfile,
                                                       this.getConfiguration(),
                                                       this.getRole(profileID),
                                                       this.getID(profileID));
                        }

                        // save the profile
                        if (saveProfile) {
                            Map      conf = this.getConfiguration();
                            String   role = this.getRole(profileID);
                            String   id   = this.getID(profileID);
                            String   type = this.getType(profileID);
                            String   saveResource;
                            String   profileType;

                            if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_GLOBAL)) {
                                saveResource = (String)conf.get(PortalConstants.CONF_GLOBALDELTA_SAVERESOURCE);
                                profileType = "global-delta";
                            } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ROLE)) {
                                saveResource = (String)conf.get(PortalConstants.CONF_ROLEDELTA_SAVERESOURCE);
                                profileType = "role-delta";
                            } else if (type.equals(PortalManagerImpl.BUILDTYPE_VALUE_ID)) {
                                saveResource = (String)conf.get(PortalConstants.CONF_USERDELTA_SAVERESOURCE);
                                profileType = "user-delta";
                            } else {
                                throw new ProcessingException("portal: No save resource defined for type '"+type+"'.");
                            }

                            // patch
                            // search for all "status/customize" nodes and set them
                            // to false
                            NodeList statusNodes = DOMUtil.selectNodeList(profile,
                                    "profile/portal-profile/content/descendant::status/customize", this.xpathProcessor);
                            if (statusNodes != null) {
                                String value;
                                for(int l=0; l < statusNodes.getLength(); l++) {
                                    value = DOMUtil.getValueOfNode(statusNodes.item(l));
                                    if (value.equals("true")) {
                                        DOMUtil.setValueOfNode(statusNodes.item(l), "false");
                                    }
                                }
                            }

                            // build delta
                            RequestState reqstate = this.getRequestState();
                            DocumentFragment delta;
                            delta = this.buildProfileDelta(type, role, id, this.getIsAdminProfile(profileID));
                            SourceParameters pars = new SourceParameters();
                            pars.setSingleParameterValue("type", profileType);
                            if (id != null) pars.setSingleParameterValue("ID", id);
                            if (role != null) pars.setSingleParameterValue("role", role);
                            pars.setSingleParameterValue("application", reqstate.getApplicationName());
                            pars.setSingleParameterValue("handler", reqstate.getHandlerName());
                            SourceUtil.writeDOM(saveResource,
                                               null,
                                               pars,
                                               delta,
                                               this.resolver,
                                               "xml");

                            if (delta.getParentNode() != null) delta.getParentNode().removeChild(delta);
                            delta = null;

                            // cache the profile
                            // The profile is only cached if it is already in the cache!
                            // Why? During login the profile is build and cached, so it is in the cache.
                            // But: If a user logs in, the profile is cached.
                            // Now the admin logs in, changes the global profile and saves it.
                            // The cache is invalidated, including the user profile.
                            // Now the user changes his profile and saves it.
                            // If it now would be cached, it would be invalid as it would
                            // not reflect the changes by the admin.
                            // But if the old profile is still in the cache, nobody
                            // has changed a profile above.
                            // Note CZ: The above is correct, but for building the delta
                            // the "previous" profile is build and cached ! Thus we can
                            // easily cache the new profile.
//                            if (this.isProfileCached(profileID, conf)) {
                            this.cacheProfile(profileID, theProfile, conf); // cache it
                            // now the hardest part, clean up the cache
                            this.cleanUpCache(type, role, conf);
//                          }

                        }

                    } // end synchronized
                }
            } catch (javax.xml.transform.TransformerException local) {
                throw new ProcessingException("TransformerException: " + local, local);
            }
        }
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END changeProfile");
        }
    }

    /**
     * Change the number of the columns
     */
    private void changeColumns(DocumentFragment profile,
                               int oldNumber,
                               int columnNumber,
                               Node[] miscNodes)
    throws javax.xml.transform.TransformerException {
        // calling method is (hopefully) synced
        if (columnNumber < oldNumber) {
            // remove columns and all coplets to the first one
            Node columnNode;
            Node firstColumn = DOMUtil.getSingleNode(profile,
                        "profile/portal-profile/content/column[@position='1']/coplets", this.xpathProcessor);
            NodeList firstColumnCoplets = DOMUtil.getNodeListFromPath(firstColumn, new String[] {"coplet"});
            int copletsCount = (firstColumnCoplets == null ? 0 : firstColumnCoplets.getLength());
            for(int i = columnNumber + 1; i <= oldNumber; i++) {
                columnNode = miscNodes[7+i];
                if (columnNode != null) {
                    NodeList coplets = DOMUtil.getNodeListFromPath(columnNode, new String[] {"coplets","coplet"});
                    Node coplet;
                    if (coplets != null && coplets.getLength() > 0) {
                        for(int m = 0; m < coplets.getLength(); m++) {
                            coplet = coplets.item(m);
                            coplet.getParentNode().removeChild(coplet);
                            copletsCount++;
                            ((Element)coplet).setAttributeNS(null, "position", "" + copletsCount);
                            firstColumn.appendChild(coplet);
                        }
                    }
                    columnNode.getParentNode().removeChild(columnNode);
                    miscNodes[7+i] = null;
                }
            }
        } else if (columnNumber <= PortalConstants.MAX_COLUMNS) {
            // add new columns
            Node contentNode = DOMUtil.getFirstNodeFromPath(profile,
                        new String[] {"profile","portal-profile","content"}, false);
            Document doc = contentNode.getOwnerDocument();
            Element newColumn;
            Element el;
            for(int i = oldNumber + 1; i <= columnNumber; i++) {
                newColumn = doc.createElementNS(null, "column");
                newColumn.setAttributeNS(null, "position", ""+i);
                miscNodes[7+i] = newColumn;
                contentNode.appendChild(newColumn);
                el = doc.createElementNS(null, "width");
                el.appendChild(doc.createTextNode("5%"));
                newColumn.appendChild(el);
                el = doc.createElementNS(null, "coplets");
                newColumn.appendChild(el);
            }
        }
    }

    /**
     * Send SAX events to the next pipeline component.
     * The node is parsed and the events are send to
     * the next component in the pipeline.
     * @param node The tree to be included.
     */
    protected void sendEvents(XMLConsumer consumer, Node node)
    throws SAXException {
        IncludeXMLConsumer.includeNode(node, consumer, consumer);
    }

    /**
     * Get all users in a document fragment with the following children:
     * <users>
     *     <user>
     *         <ID>...</ID>
     *         <role>...</role> <!-- optional -->
     *         <data>
     *         ...
     *         </data>
     *     </user>
     *     ....
     * </users>
     * The document fragment might contain further nodes at the root!
     * If <code>role</code> is <code>null</code> all users are fetched,
     * otherwise only the users for this role.
     * If also ID is not null only the single user is fetched.
     */
    private Document getUsers(String role, String ID)
    throws IOException, ProcessingException, SAXException {
        // calling method is syned
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN getUsers role="+role+", ID="+ID);
        }
        RequestState reqstate = this.getRequestState();
        Document frag = null;
        Configuration conf = reqstate.getModuleConfiguration("single-role-user-management");
        if (conf != null) {

            // get load-users resource (optional)
            Configuration child = conf.getChild("load-users", false);
            if (child != null) {
                String loadUsersResource = child.getAttribute("uri", null);
                SourceParameters loadUsersResourceParameters = SourceParameters.create(child);

                if (loadUsersResource != null) {
                    SourceParameters parameters = (loadUsersResourceParameters == null) ? new SourceParameters()

                                                                             : loadUsersResourceParameters;
                    if (reqstate.getApplicationName() != null)
                        parameters.setSingleParameterValue("application", reqstate.getApplicationName());
                    if (ID != null) {
                        parameters.setSingleParameterValue("type", "user");
                        parameters.setSingleParameterValue("ID", ID);
                    } else {
                        parameters.setSingleParameterValue("type", "users");
                    }
                    if (role != null) parameters.setSingleParameterValue("role", role);
                    frag = this.loadResource(loadUsersResource, parameters);

                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END getUsers fragment="+(frag == null ? "null" : XMLUtils.serializeNode(frag, XMLUtils.createPropertiesForXML(false))));
        }
        return frag;
    }

    /**
     * Get all roles in a document fragment with the following children:
     * <roles>
     *     <role>...</role>
     *     ....
     * </roles>
     * The document fragment might contain further nodes at the root!
     */
    private Document getRoles()
    throws IOException, ProcessingException, SAXException {
        // calling method is syned
        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("BEGIN getRoles");
        }
        Document frag = null;

        RequestState reqstate = this.getRequestState();

        Configuration conf = reqstate.getModuleConfiguration("single-role-user-management");
        if (conf != null) {

            // get load-roles resource (optional)
            Configuration child = conf.getChild("load-roles", false);
            if (child != null) {
                String loadRolesResource = child.getAttribute("uri", null);
                SourceParameters loadRolesResourceParameters = SourceParameters.create(child);
                if (loadRolesResource != null) {
                    SourceParameters parameters = (loadRolesResourceParameters == null) ? new SourceParameters()
                                                                           : loadRolesResourceParameters;
                    if (reqstate.getApplicationName() != null)
                        parameters.setSingleParameterValue("application", reqstate.getApplicationName());
                    parameters.setSingleParameterValue("type", "roles");
                    frag = this.loadResource(loadRolesResource, parameters);
                }
            }
        }

        if (this.getLogger().isDebugEnabled()) {
            this.getLogger().debug("END getRoles fragment="+frag);
        }
        return frag;
    }

    /**
     * Load XML resource
     */
    private Document loadResource(String resource,
                               SourceParameters parameters)
    throws IOException, ProcessingException, SAXException {
        Source source = null;
        try {
            source = SourceUtil.getSource(resource,
                                          null,
                                          parameters,
                                          this.resolver);
            return SourceUtil.toDOM(source);
        } catch (SourceException se) {
            throw SourceUtil.handle(se);
        } finally {
            this.resolver.release(source);
        }
    }

    /**
     * Get the SessionManager component
     */
    protected SessionManager getSessionManager()
    throws ProcessingException {
        if (this.sessionManager == null) {
            try {
                this.sessionManager = (SessionManager)this.manager.lookup(SessionManager.ROLE);
            } catch (ComponentException ce) {
                throw new ProcessingException("Error during lookup of SessionManager component.", ce);
            }
        }
        return this.sessionManager;
    }

    /**
     * Get the ContextManager component
     */
    protected ContextManager getContextManager()
    throws ProcessingException {
        if (this.contextManager == null) {
            try {
                this.contextManager = (ContextManager)this.manager.lookup(ContextManager.ROLE);
            } catch (ComponentException ce) {
                throw new ProcessingException("Error during lookup of ContextManager component.", ce);
            }
        }
        return this.contextManager;
    }

    /**
     * Get the ContextManager component
     */
    protected TransactionManager getTransactionManager()
    throws ProcessingException {
        if (this.transactionManager == null) {
            try {
                this.transactionManager = (TransactionManager)this.manager.lookup(TransactionManager.ROLE);
            } catch (ComponentException ce) {
                throw new ProcessingException("Error during lookup of TransactionManager component.", ce);
            }
        }
        return this.transactionManager;
    }

}
TOP

Related Classes of org.apache.cocoon.webapps.portal.components.PortalManagerImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.