Package org.apache.cocoon.sunshine.sunspot

Source Code of org.apache.cocoon.sunshine.sunspot.SunSpot

/*

============================================================================
                   The Apache Software License, Version 1.1
============================================================================

Copyright (C) 1999-2002 The Apache Software Foundation. All rights reserved.

Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:

1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.

4. The names "Apache Cocoon" and  "Apache Software Foundation" must  not  be
    used to  endorse or promote  products derived from  this software without
    prior written permission. For written permission, please contact
    apache@apache.org.

5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
(INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software  consists of voluntary contributions made  by many individuals
on  behalf of the Apache Software  Foundation and was  originally created by
Stefano Mazzocchi  <stefano@apache.org>. For more  information on the Apache
Software Foundation, please see <http://www.apache.org/>.

*/
package org.apache.cocoon.sunshine.sunspot;

import java.io.*;
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.excalibur.source.SourceParameters;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.parameters.Parameters;

import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.components.sax.XMLDeserializer;
import org.apache.cocoon.components.store.Store;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Session;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.xml.dom.DOMBuilder;
import org.apache.cocoon.xml.XMLConsumer;
import org.apache.cocoon.sunshine.SunShine;
import org.apache.cocoon.sunshine.components.AbstractSunShineComponent;
import org.apache.cocoon.sunshine.connector.Resource;
import org.apache.cocoon.sunshine.context.SessionContext;
import org.apache.cocoon.sunshine.context.SessionContextProvider;
import org.apache.cocoon.xml.IncludeXMLConsumer;
import org.apache.cocoon.xml.XMLUtils;
import org.apache.cocoon.sunshine.xml.XMLUtil;
import org.apache.cocoon.sunshine.sunrise.SunRise;
import org.apache.cocoon.sunshine.sunspot.context.SessionContextProviderImpl;
import org.apache.cocoon.sunshine.sunspot.sunlet.SunLetThread;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.w3c.dom.Attr;
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.w3c.dom.traversal.NodeIterator;


/**
*  This is the basis sunSpot component
*
* @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
* @version CVS $Id: SunSpot.java,v 1.5.2.2 2002/07/01 08:40:06 cziegeler Exp $
*/
public final class SunSpot
extends AbstractSunShineComponent {

    /** The avalon role */
    public static final String ROLE  = "org.apache.cocoon.sunshine.sunspot.SunSpot";


    /** Values for the buildprofile type element */
    public static final String BUILDTYPE_VALUE_BASIC  = "basic";
    public static final String BUILDTYPE_VALUE_GLOBAL = "global";
    public static final String BUILDTYPE_VALUE_ROLE   = "role";
    public static final String BUILDTYPE_VALUE_ID     = "user";

    /**
     * The request parameters for customizing sunLets
     * The request parameter is <code>REQ_PARAMETER_CMD</code> and the
     * the value is one from <code>REQ_CMD_</code> followed by
     * '_<sunLetID>_<sunLetNR>'.
     */
    public static final String REQ_PARAMETER_CMD = "sunspotcmd";

        public static final String REQ_CMD_MAXIMIZE = "maximize";
        public static final String REQ_CMD_MINIMIZE = "minimize";
        public static final String REQ_CMD_CLOSE    = "close";
        public static final String REQ_CMD_OPEN    = "open";
        public static final String REQ_CMD_HIDE    = "hide"; // synonym to close
        public static final String REQ_CMD_SHOW    = "show"; // synonym to open
        public static final String REQ_CMD_CUSTOMIZE= "customize";
        public static final String REQ_CMD_UPDATE   = "update";
        public static final String REQ_CMD_DELETE   = "delete";
        public static final String REQ_CMD_MOVE     = "move";
        public static final String REQ_CMD_NEW      = "new";
        public static final String REQ_CMD_MOVEROW  = "row";   // 1.1
        public static final String REQ_CMD_SAVEPROFILE = "save";

    /** This parameter is used for changing of profile value */
    public static final String REQ_PARAMETER_CONF = "sunspotconf";

    /** This parameter denotes the profile to be used */
    public static final String REQ_PARAMETER_PROFILE = "sunspotprofile";

    /** These parameter characterize the role and id */
    public static final String REQ_PARAMETER_ROLE = "sunspotrole";
    public static final String REQ_PARAMETER_ID   = "sunspotid";
    public static final String REQ_PARAMETER_STATE= "sunspotadmin";
    public static final String REQ_PARAMETER_SUNLET= "sunspotsunlet";
    public static final String REQ_PARAMETER_ADMIN_SUNLETS = "sunspotadmin_sunlets";

    /** This is the current role/id which is set in the context */
    public static final String ATTRIBUTE_PORTAL_ROLE = "role";
    public static final String ATTRIBUTE_PORTAL_ID   = "ID";

    /** Some states for the admin configuration */
    public static final String ATTRIBUTE_ADMIN_STATE = "adminstate";
    public static final String ATTRIBUTE_ADMIN_ROLE  = "adminrole";
    public static final String ATTRIBUTE_ADMIN_ID    = "adminid";
    public static final String ATTRIBUTE_ADMIN_SUNLETS = "adminsunlets";


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

    /** The sunRise component */
    private SunRise sunRiseComponent;

    /** Init the class,
     *  add the provider for the sunSpot context
     */
    static {
        // add the provider for the sunSpot context
        SessionContextProvider provider = new SessionContextProviderImpl();
        try {
            SunShine.addSessionContextProvider(provider, Constants.SESSION_CONTEXT_NAME);
        } catch (ProcessingException local) {
            throw new RuntimeException("Unable to register provider for sunSpot context.");
        }
    }

    /**
     * Avalon Recyclable Interface
     */
    public void recycle() {
        super.recycle();
        this.manager.release(this.profileStore);
        this.profileStore = null;
        this.manager.release(this.sunRiseComponent);
        this.sunRiseComponent = null;
    }

    /**
     * Get the profile store
     */
    public 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 profile store
     */
    public SunRise getSunRise()
    throws ProcessingException {
        if (this.sunRiseComponent == null) {
            try {
                this.sunRiseComponent = (SunRise)this.manager.lookup(SunRise.ROLE);
            } catch (ComponentException ce) {
                throw new ProcessingException("Error during lookup of sunRise component.", ce);
            }
        }
        return this.sunRiseComponent;
    }

    /**
     * Set the <code>SourceResolver</code>, objectModel <code>Map</code>,
     * used to process the request.
     *  Set up the sunShine component.
     *  This method is automatically called for each request. Do not invoke
     *  this method by hand.
     */
    public void setup(SourceResolver resolver, Map objectModel)
    throws ProcessingException, SAXException, IOException {
        // synchronized per se
        super.setup(resolver, objectModel);

        final Map map = this.getConfiguration();

        this.changeProfile();
    }

    /**
     * Configure sunSpot and check if it is allowed to see this sunlet (if it is one).
     * This is only a public wrapper for the getConfiguration method.
     */
    public void configurationTest()
    throws ProcessingException, IOException, SAXException {
        // no sync required
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN configurationTest");
        }
        Map map = this.getConfiguration();

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

    /**
     * Get the sunSpot context of the current application
     */
    public SessionContext getContext(boolean create)
    throws ProcessingException, IOException, SAXException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN getContext create="+create);
        }
        SessionContext context = null;

        final Session session = this.getSunShineComponent().getSession(false);
        if (session != null) {
            synchronized(session) {
                String appName = (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME);
                String attrName = Constants.PRIVATE_SESSION_CONTEXT_NAME;
                if (appName != null) {
                    attrName = attrName + ':' + appName;
                }
                context = this.getSunShineComponent().getContext(attrName);
                if (context == null && create == true) {

                    // create new context
                    context = this.getSunRise().createHandlerContext(attrName, null, null);

                }
            } // end synchronized
        }

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

    /**
     * Include Portal URI into stream
     */
    public void streamConfiguration(XMLConsumer consumer,
                                    String      requestURI,
                                    String      profileID,
                                    String      media,
                                    String      contextID)
    throws IOException, SAXException, ProcessingException {
        // synchronized not req.
        this.sendStartElementEvent(consumer, Constants.ELEMENT_CONFIGURATION);

        // set the portal-page uri:
        StringBuffer buffer = new StringBuffer(requestURI);
        buffer.append((requestURI.indexOf('?') == -1 ? '?' : '&'))
            .append(SunSpot.REQ_PARAMETER_PROFILE)
            .append('=')
            .append(profileID);
        String uri = this.response.encodeURL(buffer.toString());
        this.sendStartElementEvent(consumer, "uri");
        this.sendTextEvent(consumer, uri);
        this.sendEndElementEvent(consumer, "uri");

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

        this.sendStartElementEvent(consumer, "portal");
        this.sendTextEvent(consumer, portalURI);
        this.sendEndElementEvent(consumer, "portal");

        this.sendStartElementEvent(consumer, Constants.ELEMENT_PROFILE);
        this.sendTextEvent(consumer, profileID);
        this.sendEndElementEvent(consumer, Constants.ELEMENT_PROFILE);

        if (media != null) {
            this.sendStartElementEvent(consumer, "media");
            this.sendTextEvent(consumer, media);
            this.sendEndElementEvent(consumer, "media");
        }

        if (contextID != null) {
            this.sendStartElementEvent(consumer, "context");
            this.sendTextEvent(consumer, contextID);
            this.sendEndElementEvent(consumer, "context");
        }

        this.sendEndElementEvent(consumer, Constants.ELEMENT_CONFIGURATION);
    }

    /**
     * Show the admin configuration page.
     */
    public void showAdminConf(XMLConsumer consumer)
    throws SAXException, ProcessingException, IOException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN showAdminConf consumer=" + consumer);
        }
        try {
            String profileID = "global";
            String sunletID = this.request.getParameter(SunSpot.REQ_PARAMETER_SUNLET);

            SessionContext context = this.getContext(true);

            Map configuration = this.getConfiguration();

            DocumentFragment sunletsFragment = (DocumentFragment)context.getAttribute(ATTRIBUTE_ADMIN_SUNLETS);
            String command = this.request.getParameter(SunSpot.REQ_PARAMETER_ADMIN_SUNLETS);
            if (command != null && sunletsFragment != null) {
                try {
                    this.getSunShineComponent().startWritingTransaction(context);
                    // save : save sunlets base
                    // new  : new sunlet
                    // delete : use id to delete sunlet
                    // change : change the sunlet
                    //        cache : cleans the cache
                    if (command.equals("delete") == true && sunletID != null) {
                        Node sunlet = XMLUtil.getSingleNode(sunletsFragment, "sunlets-profile/sunlets/sunlet[@id='"+sunletID+"']");
                        if (sunlet != null) {
                            sunlet.getParentNode().removeChild(sunlet);
                        }
                    } else if (command.equals("change") == true && sunletID != null) {
                        Node sunlet = XMLUtil.getSingleNode(sunletsFragment, "sunlets-profile/sunlets/sunlet[@id='"+sunletID+"']");
                        if (sunlet != null) {
                            // now get the information
                            String value;

                            value = this.request.getParameter("sunspotadmin_title");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.getSingleNode(sunlet, "title"), value);

                            value = this.request.getParameter("sunspotadmin_mand");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.getSingleNode(sunlet, "configuration/mandatory"), value);

                            value = this.request.getParameter("sunspotadmin_sizable");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.getSingleNode(sunlet, "configuration/sizable"), value);

                            value = this.request.getParameter("sunspotadmin_active");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.getSingleNode(sunlet, "configuration/active"), value);

                            value = this.request.getParameter("sunspotadmin_handsize");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/handlesSizable"), value);

                            value = this.request.getParameter("sunspotadmin_handpar");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/handlesParameters"), value);

                            value = this.request.getParameter("sunspotadmin_timeout");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/timeout"), value);

                            value = this.request.getParameter("sunspotadmin_customizable");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/customizable"), value);

                            value = this.request.getParameter("sunspotadmin_persistent");
                            if (value != null) XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/persistent"), value);

                            String resource = this.request.getParameter("sunspotadmin_resource");
                            if (resource != null) {
                                Element resourceNode = (Element)XMLUtil.getSingleNode(sunlet, "resource");
                                resourceNode.getParentNode().removeChild(resourceNode);
                                resourceNode = sunlet.getOwnerDocument().createElementNS(null, "resource");
                                resourceNode.setAttributeNS(null, "uri", resource);
                                sunlet.appendChild(resourceNode);
                            }
                            resource = this.request.getParameter("sunspotadmin_cust");
                            boolean isCustom = XMLUtil.getValueAsBooleanOf(sunlet, "configuration/customizable", false);
                            if (resource != null && isCustom == true) {
                                Element resourceNode = (Element)XMLUtil.getSingleNode(sunlet, "customization");
                                if (resourceNode != null) resourceNode.getParentNode().removeChild(resourceNode);
                                resourceNode = sunlet.getOwnerDocument().createElementNS(null, "customization");
                                resourceNode.setAttributeNS(null, "uri", resource);
                                sunlet.appendChild(resourceNode);
                            }
                            if (isCustom == false) {
                                Element resourceNode = (Element)XMLUtil.getSingleNode(sunlet, "customization");
                                if (resourceNode != null) resourceNode.getParentNode().removeChild(resourceNode);
                            }

                            // transformations
                            value = this.request.getParameter("sunspotadmin_newxsl");
                            if (value != null) {
                                Element tNode = (Element)XMLUtil.selectSingleNode(sunlet, "transformation");
                                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 = this.request.getParameterNames();
                            int pos;
                            Element sNode;
                            String key;
                            while (keys.hasMoreElements() == true) {
                                key = (String)keys.nextElement();
                                if (key.startsWith("sunspotadmin_xsl_") == true) {
                                    value = key.substring(key.lastIndexOf('_')+ 1);
                                    sNode = (Element)XMLUtil.getSingleNode(sunlet, "transformation/stylesheet[position()="+value+"]");
                                    if (sNode != null) {
                                        String xslName = this.request.getParameter(key);
                                        if (xslName.equals("true") == true) xslName = "**STYLESHEET**";
                                        XMLUtil.setValueOfNode(sNode, xslName);
                                    }
                                } else if (key.startsWith("sunspotadmin_delxsl_") == true) {
                                    value = key.substring(key.lastIndexOf('_')+ 1);
                                    sNode = (Element)XMLUtil.getSingleNode(sunlet, "transformation/stylesheet[position()="+value+"]");
                                    if (sNode != null) {
                                        sNode.setAttributeNS(null, "delete", "true");
                                    }
                                }
                            }
                            NodeList delete = XMLUtil.selectNodeList(sunlet, "transformation/stylesheet[@delete]");
                            if (delete != null) {
                                for(int i=0; i < delete.getLength(); i++) {
                                    delete.item(i).getParentNode().removeChild(delete.item(i));
                                }
                            }
                        }
                    } else if (command.equals("new") == true) {
                        // first we have to invent a new sunlet id!
                        int index = 0;
                        boolean found = false;
                        Element sunlet;
                        Element subNode;

                        while (found == false) {
                            sunletID = "S"+index;
                            sunlet = (Element)XMLUtil.getSingleNode(sunletsFragment, "sunlets-profile/sunlets/sunlet[@id='"+sunletID+"']");
                            if (sunlet == null) {
                                found = true;
                            } else {
                                index++;
                            }
                        }
                        sunlet = sunletsFragment.getOwnerDocument().createElementNS(null, "sunlet");
                        sunlet.setAttributeNS(null, "id", sunletID);
                        subNode = sunlet.getOwnerDocument().createElementNS(null, "resource");
                        sunlet.appendChild(subNode);
                        subNode.setAttributeNS(null, "uri", "uri_in_sitemap");

                        String title = this.request.getParameter("sunspotadmin_title");
                        if (title == null || title.trim().length() == 0) title = "**NEW SUNLET**";
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/mandatory"), "false");
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/sizable"), "true");
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/active"), "false");
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/handlesParameters"), "true");
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "configuration/handlesSizable"), "false");
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "title"), title);
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "status/visible"), "true");
                        XMLUtil.setValueOfNode(XMLUtil.selectSingleNode(sunlet, "status/size"), "max");
                        XMLUtil.getSingleNode(sunletsFragment, "sunlets-profile/sunlets").appendChild(sunlet);
                    } else if (command.equals("save") == true) {

                        SourceParameters pars = new SourceParameters();
                        pars.setSingleParameterValue("profile", "sunlet-base");
                        pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
                        pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));

                        Resource saveResource = (Resource)configuration.get(Constants.CONF_SUNLETBASE_SAVE_RESOURCE);

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

                            this.getResourceConnector().saveXML(saveResource.getResourceType(),null,
                                 saveResource.getResourceIdentifier(),pars,
                                 sunletsFragment);

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

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

            String state = this.request.getParameter(SunSpot.REQ_PARAMETER_STATE);
            if (state == null) {
                state = (String)context.getAttribute(ATTRIBUTE_ADMIN_STATE, Constants.STATE_MAIN);
            }

            // now start producing xml:
            AttributesImpl attr = new AttributesImpl();
            consumer.startElement("", Constants.ELEMENT_ADMINCONF, Constants.ELEMENT_ADMINCONF, attr);

            context.setAttribute(ATTRIBUTE_ADMIN_STATE, state);
            consumer.startElement("", Constants.ELEMENT_STATE, Constants.ELEMENT_STATE, attr);
            consumer.characters(state.toCharArray(), 0, state.length());
            consumer.endElement("", Constants.ELEMENT_STATE, Constants.ELEMENT_STATE);

            if (state.equals(Constants.STATE_MAIN) == true) {

                DocumentFragment rolesDF = this.getSunRise().getRoles();
                Node             roles   = null;
                if (rolesDF != null) roles = XMLUtil.getSingleNode(rolesDF, "roles");
                IncludeXMLConsumer.includeNode(roles, consumer, consumer);
            }

            if (state.equals(Constants.STATE_MAIN_ROLE) == true) {

                DocumentFragment rolesDF = this.getSunRise().getRoles();
                Node             roles   = null;
                if (rolesDF != null) roles = XMLUtil.getSingleNode(rolesDF, "roles");
                IncludeXMLConsumer.includeNode(roles, consumer, consumer);

                String role = this.request.getParameter(SunSpot.REQ_PARAMETER_ROLE);
                if (role == null) {
                    role = (String)context.getAttribute(ATTRIBUTE_ADMIN_ROLE);
                }
                context.setAttribute(ATTRIBUTE_ADMIN_ROLE, role);
                if (role != null) {
                    this.sendStartElementEvent(consumer, "roleusers");
                    this.sendStartElementEvent(consumer, "name");
                    this.sendTextEvent(consumer, role);
                    this.sendEndElementEvent(consumer, "name");
                    DocumentFragment userDF = this.getSunRise().getUsers(role, null);
                    Node             users = null;
                    if (userDF != null) users = XMLUtil.getSingleNode(userDF, "users");
                    IncludeXMLConsumer.includeNode(users, consumer, consumer);
                    this.sendEndElementEvent(consumer, "roleusers");
                }
            }

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

            if (state.equals(Constants.STATE_ROLE) == true) {
                String role = this.request.getParameter(SunSpot.REQ_PARAMETER_ROLE);
                if (role == null) {
                    role = (String)context.getAttribute(ATTRIBUTE_ADMIN_ROLE);
                }
                context.setAttribute(ATTRIBUTE_ADMIN_ROLE, role);
                if (role != null) {
                    consumer.startElement("", Constants.ELEMENT_ROLE, Constants.ELEMENT_ROLE, attr);
                    consumer.characters(role.toCharArray(), 0, role.length());
                    consumer.endElement("", Constants.ELEMENT_ROLE, Constants.ELEMENT_ROLE);
                    profileID = this.getProfileID(SunSpot.BUILDTYPE_VALUE_ROLE, role, null, true);
                    Map profile = this.retrieveProfile(profileID);
                    if (profile == null) {
                        this.createProfile(context, SunSpot.BUILDTYPE_VALUE_ROLE, role, null, true);
                        profile = this.retrieveProfile(profileID);
                    }
                    this.showPortal(consumer, true, context, profile, profileID);
                }
            }
            if (state.equals(Constants.STATE_USER) == true) {
                String role = this.request.getParameter(SunSpot.REQ_PARAMETER_ROLE);
                String id   = this.request.getParameter(SunSpot.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("", Constants.ELEMENT_ROLE, Constants.ELEMENT_ROLE, attr);
                    consumer.characters(role.toCharArray(), 0, role.length());
                    consumer.endElement("", Constants.ELEMENT_ROLE, Constants.ELEMENT_ROLE);
                    consumer.startElement("", Constants.ELEMENT_ID, Constants.ELEMENT_ID, attr);
                    consumer.characters(id.toCharArray(), 0, id.length());
                    consumer.endElement("", Constants.ELEMENT_ID, Constants.ELEMENT_ID);

                    profileID = this.getProfileID(SunSpot.BUILDTYPE_VALUE_ID, role, id, true);
                    Map profile = this.retrieveProfile(profileID);
                    if (profile == null) {
                        this.createProfile(context, SunSpot.BUILDTYPE_VALUE_ID, role, id, true);
                        profile = this.retrieveProfile(profileID);
                    }
                    this.showPortal(consumer, true, context, profile, profileID);
                }
            }
            // one sunlet
            if (state.equals(Constants.STATE_SUNLET) == true) {
                if (sunletsFragment != null && sunletID != null) {
                    Node sunlet = XMLUtil.getSingleNode(sunletsFragment, "sunlets-profile/sunlets/sunlet[@id='"+sunletID+"']");
                    if (sunlet != null) {
                        IncludeXMLConsumer.includeNode(sunlet, consumer, consumer);
                    }
                } else {
                    state = Constants.STATE_SUNLETS;
                }
            }
            if (state.equals(Constants.STATE_SUNLETS) == true) {
                consumer.startElement("", Constants.ELEMENT_SUNLETS, Constants.ELEMENT_SUNLETS, attr);

                // load the base sunlets profile
                if (sunletsFragment == null) {
                    SourceParameters pars = new SourceParameters();
                    pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
                    Resource res = (Resource)configuration.get(Constants.CONF_SUNLETBASE_RESOURCE);
                    if (res == null) {
                        throw new ProcessingException("No configuration for sunSpot-sunlet base profile found.");
                    }
                    sunletsFragment = this.loadXML(res.getResourceType(), null,
                                                   res.getResourceIdentifier(), pars,
                                                   "Error during loading of sunLet base.");
                    context.setAttribute(ATTRIBUTE_ADMIN_SUNLETS, sunletsFragment);
                }
                IncludeXMLConsumer.includeNode(XMLUtil.selectSingleNode(sunletsFragment,
                                   "sunlets-profile"), consumer, consumer);
                consumer.endElement("", Constants.ELEMENT_SUNLETS, Constants.ELEMENT_SUNLETS);
            }

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

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

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

    /**
     * Get the status profile
     */
    public Element getStatusProfile()
    throws SAXException, IOException, ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN getStatusProfile");
        }
        SessionContext context = this.getContext(true);
        String profileID = null;
        Map storedProfile = null;
        Element statusProfile = null;

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

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

        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("END getStatusProfile statusProfile="+(statusProfile == null ? "null" : XMLUtils.serializeNodeToXML(statusProfile)));
        }
        return statusProfile;
    }

    /**
     * Show the portal.
     * The portal is included in the current stream.
     */
    public void showPortal(XMLConsumer consumer,
                           boolean configMode,
                           boolean adminProfile)
    throws SAXException, ProcessingException, IOException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN showPortal consumer=" + consumer+", configMode="+
                             configMode+", adminProfile="+adminProfile);
        }
        SessionContext context = this.getContext(true);
        String profileID = null;
        Map storedProfile = null;

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

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

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

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

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

        // first: layout of portal
        consumer.startElement("", Constants.ELEMENT_PORTAL, Constants.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("", Constants.ELEMENT_PORTAL, Constants.ELEMENT_PORTAL);

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

        consumer.endElement("", Constants.ELEMENT_LAYOUT, Constants.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() == true) {
            this.getLogger().debug("BEGIN showPortal consumer=" + consumer+", configMode="+configMode+", context="+context+
                ", profile="+storedProfile);
        }
        try {
            this.getSunShineComponent().startReadingTransaction(context);

            DocumentFragment profile;
            Map              defaultSunlets;
            Map              mediaSunlets;
            Map              portalLayouts;
            Map              sunletLayouts;
            Node[]           miscNodes;
            String           mediaType = (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_MEDIA_TYPE);

            profile = (DocumentFragment)storedProfile.get(Constants.PROFILE_PROFILE);
            portalLayouts = (Map)storedProfile.get(Constants.PROFILE_PORTAL_LAYOUTS);
            sunletLayouts = (Map)storedProfile.get(Constants.PROFILE_SUNLET_LAYOUTS);
            miscNodes = (Node[])storedProfile.get(Constants.PROFILE_MISC_POINTER);
            defaultSunlets = (Map)storedProfile.get(Constants.PROFILE_DEFAULT_SUNLETS);
            mediaSunlets = (Map)storedProfile.get(Constants.PROFILE_MEDIA_SUNLETS);
            if (profile == null ||
                defaultSunlets == null ||
                mediaSunlets == null ||
                portalLayouts == null ||
                sunletLayouts == null ||
                miscNodes == null) {
                throw new ProcessingException("sunSpot: No portal profile found.");
            }

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

            Boolean boolValue = (Boolean)config.get(Constants.CONF_PARALLEL_SUNLETS);
            if (boolValue != null) processSunletsParallel = boolValue.booleanValue();
            Long longValue = (Long)config.get(Constants.CONF_SUNLET_TIMEOUT);
            if (longValue != null) defaultSunletTimeout = longValue.longValue();

            Element element;

            // now start producing xml:
            AttributesImpl attr = new AttributesImpl();
            if (configMode == true) {
                this.sendStartElementEvent(consumer, Constants.ELEMENT_PORTALCONF);
            } else {
                this.sendStartElementEvent(consumer, Constants.ELEMENT_PORTAL);
            }

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

            // LAYOUT:
            if (configMode == true) {
                IncludeXMLConsumer.includeNode(XMLUtil.getFirstNodeFromPath(profile, new String[] {"profile","layout-profile"}, false),
                     consumer, consumer);
                // SunletsConfiguration (only for configMode)
                IncludeXMLConsumer.includeNode(XMLUtil.getFirstNodeFromPath(profile, new String[] {"profile","sunlets-profile"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(XMLUtil.getFirstNodeFromPath(profile, new String[] {"profile","type-profile","typedefs"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(XMLUtil.getFirstNodeFromPath(profile, new String[] {"profile","portal-profile"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(XMLUtil.getFirstNodeFromPath(profile, new String[] {"profile","personal-profile"}, false),
                     consumer, consumer);
                IncludeXMLConsumer.includeNode(XMLUtil.getFirstNodeFromPath(profile, new String[] {"profile","status-profile"}, false),
                     consumer, consumer);
            } else {
                SunSpot.streamLayoutProfile(consumer, portalLayouts, sunletLayouts, mediaType);
            }
            // END LAYOUT

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

                String sunletNotAvailableMessage = "The sunLet is currently not available.";
                Node messages = miscNodes[Constants.PROFILE_MISC_MESSAGES_NODE];
                if (messages != null) {
                    messages = XMLUtil.getFirstNodeFromPath(messages, new String[] {"sunlet_not_available"}, false);
                    if (messages != null) sunletNotAvailableMessage = XMLUtil.getValueOfNode(messages,
                         sunletNotAvailableMessage);
                }

                // LOAD SUNLETS
                List[] sunletContents;

                sunletContents = (List[])context.getAttribute(Constants.ATTRIBUTE_SUNLET_REPOSITORY);
                if (sunletContents == null) {
                    sunletContents = new List[Constants.MAX_COLUMNS+2];
                    context.setAttribute(Constants.ATTRIBUTE_SUNLET_REPOSITORY, sunletContents);
                }
                if (sunletContents[0] == null) {
                    sunletContents[0] = new ArrayList(1);
                } else {
                    sunletContents[0].clear();
                }
                if (sunletContents[1] == null) {
                    sunletContents[1] = new ArrayList(1);
                } else {
                    sunletContents[1].clear();
                }

                // test for header
                String value;
                value = XMLUtil.getValueOfNode(miscNodes[Constants.PROFILE_MISC_HEADER_NODE]);
                if (value != null && new Boolean(value).booleanValue() == true) {
                    element = (Element)miscNodes[Constants.PROFILE_MISC_HEADER_CONTENT_NODE];
                    if (element != null) {
                        this.loadSunlets(element,
                                         defaultSunlets,
                                         mediaSunlets,
                                         sunletContents[0],
                                         processSunletsParallel,
                                         defaultSunletTimeout,
                                         statusProfile);
                    }
                }

                // content
                value = XMLUtil.getValueOfNode(miscNodes[Constants.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 > Constants.MAX_COLUMNS) {
                        throw new ProcessingException("sunSpot: Maximum number of columns supported is: "+Constants.MAX_COLUMNS);
                    }

                    for(int colindex = 1; colindex <= columns; colindex++) {
                        if (sunletContents[colindex+1] == null) {
                            sunletContents[colindex+1] = new ArrayList(10);
                        } else {
                            sunletContents[colindex+1].clear();
                        }
                        columnElement = (Element)miscNodes[7 + colindex];
                        element = (Element)XMLUtil.getFirstNodeFromPath(columnElement, new String[] {"sunlets"}, false);
                        if (element != null) {
                            this.loadSunlets(element,
                                             defaultSunlets,
                                             mediaSunlets,
                                             sunletContents[colindex+1],
                                             processSunletsParallel,
                                             defaultSunletTimeout,
                                             statusProfile);
                        }

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

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

                // test for footer
                value = XMLUtil.getValueOfNode(miscNodes[Constants.PROFILE_MISC_FOOTER_NODE]);
                if (value != null && new Boolean(value).booleanValue() == true) {
                    element = (Element)miscNodes[Constants.PROFILE_MISC_FOOTER_CONTENT_NODE];
                    if (element != null) {
                        this.loadSunlets(element,
                                         defaultSunlets,
                                         mediaSunlets,
                                         sunletContents[1],
                                         processSunletsParallel,
                                         defaultSunletTimeout,
                                         statusProfile);
                    }
                }
                // END LOAD SUNLETS

                // DESIGN
                // test for header
                if (sunletContents[0].size() > 0) {
                    consumer.startElement("", "header", "header", attr);
                    this.processSunletList(sunletContents[0], consumer, sunletNotAvailableMessage, defaultSunletTimeout);
                    consumer.endElement("", "header", "header");
                }

                // content
                value = XMLUtil.getValueOfNode(miscNodes[Constants.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);
                    this.sendStartElementEvent(consumer, "columns", attr);
                    attr.clear();

                    int columns = new Integer(value).intValue();
                    if (columns > Constants.MAX_COLUMNS) {
                        throw new ProcessingException("sunSpot: Maximum number of columns supported is: "+Constants.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 = XMLUtil.getValueOf(columnElement, "width");
                        if (value == null) {
                            width[colindex-1] = "" + normalWidth + "%";
                        } else {
                            width[colindex-1] = value;
                        }
                    }

                    List sunletsOfColumn;

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

                        this.processSunletList(sunletContents[colindex+1], consumer, sunletNotAvailableMessage, defaultSunletTimeout);

                        this.sendEndElementEvent(consumer, "column");
                    }
                    this.sendEndElementEvent(consumer, "columns");
                } else {
                    attr.addAttribute("", "number", "number", "CDATA", "0");
                    this.sendStartElementEvent(consumer, "columns", attr);
                    this.sendEndElementEvent(consumer, "columns");
                    attr.clear();
                }

                // test for footer
                if (sunletContents[1].size() > 0) {
                    this.sendStartElementEvent(consumer, "footer");
                    this.processSunletList(sunletContents[1], consumer, sunletNotAvailableMessage, defaultSunletTimeout);
                    this.sendEndElementEvent(consumer, "footer");
                }
                // END DESIGN

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

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

            if (configMode == true) {
                this.sendEndElementEvent(consumer, Constants.ELEMENT_PORTALCONF);
            } else {
                this.sendEndElementEvent(consumer, Constants.ELEMENT_PORTAL);
            }

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


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

            SessionContext context = this.getContext(true);
            try {
                this.getSunShineComponent().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 sunSpot found.");
                }

                // is the ID profile cached?
                if (type.equals(SunSpot.BUILDTYPE_VALUE_ID) == true) {
                    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(SunSpot.BUILDTYPE_VALUE_ID) == true) {
                        doID = true;
                        previousID = this.getProfileID(SunSpot.BUILDTYPE_VALUE_ROLE, role, null, adminProfile);
                        theProfile = this.getCachedProfile(previousID, config);
                        if (theProfile == null) {
                            doRole = true;
                            previousID = this.getProfileID(SunSpot.BUILDTYPE_VALUE_GLOBAL, null, null, adminProfile);
                            theProfile = this.getCachedProfile(previousID, config);
                            if (theProfile == null) {
                                doGlobal = true;
                                previousID = this.getProfileID(SunSpot.BUILDTYPE_VALUE_BASIC, null, null, adminProfile);
                                theProfile = this.getCachedProfile(previousID, config);
                            }
                        }
                    } else if (type.equals(SunSpot.BUILDTYPE_VALUE_ROLE) == true) {
                        theProfile = this.getCachedProfile(profileID, config);
                        if (theProfile == null) {
                            doRole = true;
                            previousID = this.getProfileID(SunSpot.BUILDTYPE_VALUE_GLOBAL, null, null, adminProfile);
                            theProfile = this.getCachedProfile(previousID, config);
                            if (theProfile == null) {
                                doGlobal = true;
                                previousID = this.getProfileID(SunSpot.BUILDTYPE_VALUE_BASIC, null, null, adminProfile);
                                theProfile = this.getCachedProfile(previousID, config);
                            }
                        }
                    } else if (type.equals(SunSpot.BUILDTYPE_VALUE_GLOBAL) == true) {
                        theProfile = this.getCachedProfile(profileID, config);
                        if (theProfile == null) {
                            doGlobal = true;
                            previousID = this.getProfileID(SunSpot.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 == true) {
                        // build the base level
                        profile = this.buildBaseProfile(config, adminProfile);
                        profileRoot = (Element)profile.getFirstChild();
                        theProfile.put(Constants.PROFILE_PROFILE, profile);
                        this.cacheProfile(this.getProfileID(SunSpot.BUILDTYPE_VALUE_BASIC, null, null, adminProfile), theProfile, config);
                    } else {
                        profile = (DocumentFragment)theProfile.get(Constants.PROFILE_PROFILE);
                        profileRoot = (Element)profile.getFirstChild();
                    }

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

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

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

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

                    if (type.equals(SunSpot.BUILDTYPE_VALUE_BASIC) == false) {
                        this.buildRunProfile(theProfile, context, profile);

                        theProfile.put(Constants.PROFILE_PORTAL_LAYOUTS,
                               this.buildPortalLayouts(context, profile));
                        theProfile.put(Constants.PROFILE_SUNLET_LAYOUTS,
                               this.buildSunletLayouts(context, profile));

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

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

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

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

        if (this.getLogger().isDebugEnabled() == true) {
            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() == true) {
            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(SunSpot.BUILDTYPE_VALUE_ID) == true) {
            baseType = SunSpot.BUILDTYPE_VALUE_ROLE;
            baseRole = role;
            baseID   = null;
            rootElementName = "user-delta";
        } else if (type.equals(SunSpot.BUILDTYPE_VALUE_ROLE) == true) {
            baseType = SunSpot.BUILDTYPE_VALUE_GLOBAL;
            baseRole = null;
            baseID   = null;
            rootElementName = "role-delta";
        } else if (type.equals(SunSpot.BUILDTYPE_VALUE_GLOBAL) == true) {
            baseType = SunSpot.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(Constants.PROFILE_PROFILE);
        delta = originalFragment.getOwnerDocument().createDocumentFragment();
        delta.appendChild(delta.getOwnerDocument().createElementNS(null, rootElementName));

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

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

        // check for the highes sunlet number
        Node[] miscNodes = (Node[])originalProfile.get(Constants.PROFILE_MISC_POINTER);
        Element lastSunlet = (Element)miscNodes[Constants.PROFILE_MISC_LAST_SUNLET_NODE];
        if (lastSunlet != null) {
            String lastNumber = ((Element)lastSunlet).getAttributeNS(null, "number");
            if (lastNumber != null) {
                int value = new Integer(lastNumber).intValue();
                if (value > 1000000) {
                    NodeList sunlets = XMLUtil.selectNodeList(delta, "profile/portal-profile/descendant::sunlet[@id and @number]");
                    if (sunlets != null) {
                        Element sunletNode;
                        String oldNumber;
                        String sunletId;
                        Element statusNode;
                        boolean sunletsChanged = false;
                        for(int i=0; i <sunlets.getLength(); i++) {
                            sunletNode = (Element)sunlets.item(i);
                            oldNumber = sunletNode.getAttributeNS(null, "number");
                            sunletId = sunletNode.getAttributeNS(null, "id");
                            statusNode = (Element)XMLUtil.getSingleNode(delta, "status-profile/customization/sunlet[@id='"+sunletId+"' and @number='"+oldNumber+"']");
                            sunletNode.setAttributeNS(null, "number", ""+(i+1));
                            if (statusNode != null) {
                                statusNode.setAttributeNS(null, "number", ""+(i+1));
                                sunletsChanged = true;
                            }
                        }
                        if (sunletsChanged == true) {
                            this.saveUserStatusProfile(originalProfile,
                                   this.getConfiguration(), role, id, adminProfile);
                        }
                    }
                }
            }
        }

        // Last part: strip type information
        NodeList typeElements = XMLUtil.selectNodeList(delta, "descendant::*[@formpath and @formdescription and @formtype]");
        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() == true) {
            this.getLogger().debug("END buildProfileDelta delta="+delta);
        }
        return delta;
    }

    /**
     * Make the difference :-)
     */
    private void diff(DocumentFragment original,
                      DocumentFragment base,
                      String           path,
                      Element          deltaElement)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is already synchronized
        Element originalRoot = (Element)XMLUtil.getSingleNode(original, path);
        Element baseRoot = (Element)XMLUtil.getSingleNode(base, path);
        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 == false && m < l) {
                    currentBaseNode = baseChilds.item(m);
                    if (currentBaseNode.getNodeType() == Node.ELEMENT_NODE
                        && currentBaseNode.getNodeName().equals(currentOrigNode.getNodeName()) == true) {

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

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

                        // 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 = XMLUtil.getValueOfNode(currentBaseNode, "").trim();
                        String originalString = XMLUtil.getValueOfNode(currentOrigNode, "").trim();

                        if (baseString.equals(originalString) == false) {
                            // 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 = XMLUtil.getNodeListFromPath(contextElement, new String[] {currentElement.getNodeName()});
                                foundChild = false;
                                cIndex = 0;
                                if (possibleChilds != null) {
                                    while (foundChild == false && cIndex < possibleChilds.getLength()) {
                                        foundChild = this.compareAttributes(currentElement, possibleChilds.item(cIndex));
                                        if (foundChild == false) cIndex++;
                                    }
                                }
                                if (foundChild == true) {
                                    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);
                    }
                }
            }

        }

    }

    /**
     * Builds the key for caching
     */
    public String getProfileID(String type,
                                String role,
                                String id,
                                boolean adminProfile) {
        // No sync required
        StringBuffer key = new StringBuffer((adminProfile == true ? "aprofile:" : "uprofile:"));
        key.append((String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME))
           .append('|')
           .append((String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME))
           .append(':')
           .append(type);

        if (type.equals(SunSpot.BUILDTYPE_VALUE_ROLE) == true
            || type.equals(SunSpot.BUILDTYPE_VALUE_ID) == true) {
            role = XMLUtil.encode(role);
            key.append('_').append(role.length()).append('_').append(role);
        }
        if (type.equals(SunSpot.BUILDTYPE_VALUE_ID) == true) {
            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() == true) {
            this.getLogger().debug("BEGIN storeProfile id="+profileID+", profile="+profile);
        }

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

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

    /**
     * Retrieve the profil
     */
    public Map retrieveProfile(String profileID)
    throws ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN retrieveProfile id="+profileID);
        }
        Session session = this.getSunShineComponent().getSession(true);
        Map result;
        synchronized(session) {
            result = (Map)session.getAttribute(profileID);
        }

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

        return result;
    }

    /**
     * Check if the profile is already cached
     */
    private boolean isProfileCached(String profileID, Map configuration)
    throws ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN isProfileCached id="+profileID+", configuration="+configuration);
        }
        boolean result = false;

        if (configuration != null && this.getIsAdminProfile(profileID) == false) {
            String storePrefix = (String)configuration.get(Constants.CONF_PROFILE_CACHE);
            if (storePrefix != null) {
                String key = profileID.substring(1);
                result = this.getProfileStore().containsKey(key);
            }
        }

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

    /**
     * Cache the profile (if cache is turned on)
     */
    private void cacheProfile(String profileID,
                              Map profile,
                              Map configuration) {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN cacheProfile id="+profileID+", profile="+profile);
        }
        try {
            if (configuration != null && this.getIsAdminProfile(profileID) == false) {
                String storePrefix = (String)configuration.get(Constants.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() == true) {
            this.getLogger().debug("END cacheProfile");
        }
    }

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

        Map result = null;

        try {
            if (configuration != null && this.getIsAdminProfile(profileID) == false) {
                final String storePrefix = (String)configuration.get(Constants.CONF_PROFILE_CACHE);
                if (storePrefix != null) {
                    final String key = profileID.substring(1);
                    final Store store = this.getProfileStore();
                    if (store.containsKey(key) == true) {
                        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() == true) {
            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() == true) {
            this.getLogger().debug("BEGIN cleanUpCache type="+type+", role="+role+", config="+configuration);
        }
        if (configuration != null
            && type != null
            && type.equals(SunSpot.BUILDTYPE_VALUE_ID) == false) {
            String storePrefix = (String)configuration.get(Constants.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(SunSpot.BUILDTYPE_VALUE_BASIC) == true ||
                    type.equals(SunSpot.BUILDTYPE_VALUE_GLOBAL) == true) {
                    if (type.equals(SunSpot.BUILDTYPE_VALUE_BASIC) == true) {
                        deleteGlobal = this.getProfileID(SunSpot.BUILDTYPE_VALUE_GLOBAL, null, null, false).substring(1);
                    }
                    deleteRole = this.getProfileID(SunSpot.BUILDTYPE_VALUE_GLOBAL, null, null, false);
                    deleteRole = deleteRole.substring(1, deleteRole.lastIndexOf(':')+1) + SunSpot.BUILDTYPE_VALUE_ROLE;
                    deleteUser = this.getProfileID(SunSpot.BUILDTYPE_VALUE_GLOBAL, null, null, false);
                    deleteUser = deleteUser.substring(1, deleteUser.lastIndexOf(':')+1) + SunSpot.BUILDTYPE_VALUE_ID;
                } else { // role
                    deleteGlobal = this.getProfileID(SunSpot.BUILDTYPE_VALUE_ROLE, role, null, false).substring(1);
                    deleteUser = this.getProfileID(SunSpot.BUILDTYPE_VALUE_ID, role, "a", false);
                    deleteUser = deleteUser.substring(1, deleteUser.length()-1);
                }

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

                delete = this.getProfileID(SunSpot.BUILDTYPE_VALUE_GLOBAL, null, null, false);
                delete = delete.substring(1, delete.lastIndexOf(':') + 1);

                while (keys.hasMoreElements() == true) {
                    currentKey = (String)keys.nextElement();
                    if (currentKey.startsWith(delete) == true) {
                        store.remove(currentKey);
                    }
                }
            }
        }

        if (this.getLogger().isDebugEnabled() == true) {
            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 SAXException, ProcessingException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN buildRunProfile context="+context+", profile="+baseProfile);
        }

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

        profileMap.put(Constants.PROFILE_DEFAULT_SUNLETS, defaultSunlets);
        profileMap.put(Constants.PROFILE_MEDIA_SUNLETS, mediaSunlets);

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

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

        profileMap.put(Constants.PROFILE_MISC_POINTER, miscNodes);

        // build sunlet configs
        NodeList sunlets;
        int      i, l;
        Element  configElement;
        String   sunletID;
        String   sunletMedia;

        sunlets = XMLUtil.getNodeListFromPath(baseProfile, new String[] {"profile","sunlets-profile","sunlets","sunlet"});

        if (sunlets != null) {

            l = sunlets.getLength();
            for(i = 0; i < l; i++) {
                configElement = (Element)sunlets.item(i);
                if (XMLUtil.getValueAsBooleanOf(configElement, "configuration/active") == true) {

                    sunletID = configElement.getAttributeNS(null, "id");
                    if (configElement.hasAttributeNS(null, "media") == true) {
                        sunletMedia = configElement.getAttributeNS(null, "media");
                        mediaMap = (Map)mediaSunlets.get(sunletMedia);
                        if (mediaMap != null) {
                            mediaMap.put(sunletID, configElement);
                        }
                    } else {
                        sunletMedia = null;
                        defaultSunlets.put(sunletID, configElement);
                    }

                    // Now: add the sunlet if mandatory and missing
                    if (XMLUtil.getValueAsBooleanOf(configElement, "configuration/mandatory") == true) {
                        // get all sunlet instances
                        NodeList sunletElements;

                        // the next is crap, but it works....
                        // search all sunlets (columns, header, footer)
                        if (sunletMedia == null) {
                            sunletElements = XMLUtil.selectNodeList(baseProfile,
                                "profile/portal-profile/content/column/sunlets/sunlet[@id='"+sunletID+"' and not(@media)]");
                        } else {
                            sunletElements = XMLUtil.selectNodeList(baseProfile,
                                "profile/portal-profile/content/column/sunlets/sunlet[@id='"+sunletID+"' and media='"+sunletMedia+"']");
                        }

                        if (sunletElements == null || sunletElements.getLength() == 0) {
                            if (sunletMedia == null) {
                                sunletElements = XMLUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/header/sunlet[@id='"+sunletID+"' and not(@media)]");
                            } else {
                                sunletElements = XMLUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/header/sunlet[@id='"+sunletID+"' and media='"+sunletMedia+"']");
                            }
                        }

                        if (sunletElements == null || sunletElements.getLength() == 0) {
                            if (sunletMedia == null) {
                                sunletElements = XMLUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/footer/sunlet[@id='"+sunletID+"' and not(@media)]");
                            } else {
                                sunletElements = XMLUtil.selectNodeList(baseProfile,
                                   "profile/portal-profile/content/footer/sunlet[@id='"+sunletID+"' and media='"+sunletMedia+"']");
                            }
                        }

                        if (sunletElements == null || sunletElements.getLength() == 0) {
                            // mandatory sunlet is not configured, so add it to the first column
                            Node content = XMLUtil.getSingleNode(baseProfile,
                                  "profile/portal-profile/content/column[@position='1']/sunlets");
                            if (content == null)
                                throw new ProcessingException("Element not found: portal-profile/content/column/sunlets");
                            Element el = content.getOwnerDocument().createElementNS(null, "sunlet");
                            el.setAttributeNS(null, "id", sunletID);
                            if (sunletMedia != null) {
                                el.setAttributeNS(null, "media", sunletMedia);
                            }
                            // Set position attribute
                            NodeList childs = XMLUtil.getNodeListFromPath(content, new String[] {"sunlet"});
                            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 = XMLUtil.getValueAsBooleanOf(configElement, "status/visible");
                            int si, sl;
                            sl = sunletElements.getLength();
                            si = 0;
                            found = false;
                            while (si < sl && found == false) {
                                found = XMLUtil.getValueAsBooleanOf(sunletElements.item(si),
                                           "status/visible", origVisible);
                                si++;
                            }
                            if (found == false) {
                                // set first to visible
                                // first: is status node available
                                Node statusElem = XMLUtil.getFirstNodeFromPath(sunletElements.item(0), new String[] {"status"}, false);
                                if (statusElem == null) {
                                    statusElem = sunletElements.item(0).getOwnerDocument().createElementNS(null, "status");
                                    sunletElements.item(0).appendChild(statusElem);
                                }
                                // second: is visible node available
                                Node visibleElem = XMLUtil.getFirstNodeFromPath(statusElem, new String[] {"visible"}, false);
                                if (visibleElem == null) {
                                    visibleElem = statusElem.getOwnerDocument().createElementNS(null, "visible");
                                    statusElem.appendChild(visibleElem);
                                }
                                // remove old childs
                                while (visibleElem.hasChildNodes() == true) {
                                    visibleElem.removeChild(visibleElem.getFirstChild());
                                }
                                visibleElem.appendChild(statusElem.getOwnerDocument().createTextNode("true"));
                            }
                        }
                    }
                }
            }
        }

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

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

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

                    String numberValue = currentSunlet.getAttributeNS(null, "number");
                    if (numberValue == null || numberValue.equals("") == true) {
                        // create unique number attribute
                        currentSunlet.setAttributeNS(null, "number", ""+number);
                        miscNodes[Constants.PROFILE_MISC_LAST_SUNLET_NODE] = currentSunlet;
                        number++;
                    } else {
                        int currentNumber = new Integer(numberValue).intValue();
                        if (currentNumber > highestSunletNumber) {
                            highestSunletNumber = currentNumber;
                            number = highestSunletNumber+1;
                            miscNodes[Constants.PROFILE_MISC_LAST_SUNLET_NODE] = currentSunlet;
                        }
                    }
                    // update status
                    configElement = this.getSunletConfiguration(currentSunlet.getAttributeNS(null, "id"),
                                                      defaultSunlets,
                                                      mediaSunlets);
                    if (configElement != null) {
                        statusSunletElement = (Element)XMLUtil.selectSingleNode(configElement, "status");
                        statusConfigList = XMLUtil.selectNodeList(statusSunletElement, "*");
                        statusSunletList = XMLUtil.selectNodeList(currentSunlet, "status/*");
                        // first test if each status is included in the config
                        if (statusSunletList != null) {
                            list_length = statusSunletList.getLength();
                            for(list_index = list_length-1; list_index >= 0; list_index--) {
                                currentStatus = statusSunletList.item(list_index);
                                if (currentStatus.getNodeType() == Node.ELEMENT_NODE) {
                                    if (XMLUtil.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 (XMLUtil.getFirstNodeFromPath(statusSunletElement, new String[] {currentStatus.getNodeName()}, false) == null) {
                                        // create a new element
                                        statusSunletElement.appendChild(statusSunletElement.getOwnerDocument().importNode(currentStatus, true));
                                    }
                                }
                            }
                        }
                    } else {
                        // sunlet not in configuration
                        // adopt position of following sunlets and then remove
                        String posAttr = currentSunlet.getAttributeNS(null, "position");
                        NodeList followUps = XMLUtil.selectNodeList(currentSunlet.getParentNode(), "sunlet[@position > '"+posAttr+"']");
                        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);
                            }
                        }
                        currentSunlet.getParentNode().removeChild(currentSunlet);
                    }
                }
            }
        }

        if (this.getLogger().isDebugEnabled() == true) {
            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 SAXException, ProcessingException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN buildTypeProfile context="+context+", profile="+baseProfile);
        }
        List list = new ArrayList(25);
        List confList = new ArrayList(25);

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

        Element typeElement;

        typeElement = (Element)XMLUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","type-profile","elements"}, false);
        if (typeElement != null) {
            if (typeElement.hasChildNodes() == true)
                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(@*)]") == true) {
                    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)XMLUtil.getFirstNodeFromPath(baseProfile, new String[] {"profile","layout-profile","portal","columns","number"}, false);
        String layoutValue = XMLUtil.getValueOfNode(layoutColumnsNode);
        int layoutColumns = 0;
        if (layoutValue != null && new Integer(layoutValue).intValue() > 0) {
            layoutColumns = new Integer(layoutValue).intValue();
        }
        NodeList columnNodes = XMLUtil.selectNodeList(baseProfile, "profile/portal-profile/content/column[@position]");
        int columns = columnNodes.getLength();
        if (columns != layoutColumns) {
            if (layoutColumnsNode.hasAttributeNS(null, "formtype") == true) {
                XMLUtil.setValueOfNode(layoutColumnsNode, ""+columns);
            } else {
                this.changeColumns(baseProfile,
                                   columns,
                                   layoutColumns,
                                   (Node[])theProfile.get(Constants.PROFILE_MISC_POINTER));
                this.setTypeInfo(baseProfile,
                                 (List)theProfile.get(Constants.PROFILE_TYPE_PATHS),
                                 (List)theProfile.get(Constants.PROFILE_TYPE_CONF_PATHS));
            }
        }

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

    /**
     * Set the tpe information
     */
    private void setTypeInfo(DocumentFragment baseProfile, List paths, List confPaths)
    throws SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled() == true) {
            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 = XMLUtil.selectNodeList(baseProfile, currentPath);
                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",
                                     SunSpot.REQ_PARAMETER_CONF + '.' + i + '.' + m);
                            ((Element)currentNode).setAttributeNS(null, "formdescription", description);
                            changed = true;
                        }
                    }
                }
                if (changed == true && confPaths != null) {
                    currentPath = (String)confPaths.get(i);
                    nodes = XMLUtil.selectNodeList(baseProfile, currentPath);
                    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",
                                     SunSpot.REQ_PARAMETER_CONF + '.' + i + '.' + m);
                            }
                        }
                    }
                }
            }
        }

        if (this.getLogger().isDebugEnabled() == true) {
            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() == true) {
                    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") == false
                            && nnm.item(ia).getNodeName().equals("description") == false) {
                            if (first == false) expression.append(" and ");
                            if (nnm.item(ia).getNodeValue().equals("*") == false) {
                                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 == true) {
                        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() == true) {
                        this.addTypePath(list, current.getChildNodes(), newPath.toString());
                    }
                }
            }
        }
    }

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

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

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

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

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

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

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

        deltaRoot = XMLUtil.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)XMLUtil.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() == true) {
            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)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled() == true) {
           this.getLogger().debug("BEGIN addProfilePart root=" + profileRoot + ", delta=" + delta + ", deltaRoot:" + deltaRootTagName + ", delta: " + deltaTag);
        }
        Node     deltaRoot   = null;
        Node     oldNode     = null;

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

        if (deltaRoot != null) {
            // root tag found in delta found, now search root tag in profile
            oldNode = (Element)XMLUtil.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() == true) {
            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 == false && m < l) {
                    currentProfile = profileChilds.item(m);
                    if (currentProfile.getNodeType() == Node.ELEMENT_NODE
                        && currentProfile.getNodeName().equals(currentDelta.getNodeName()) == true) {

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

                    // do we have elements as children or text?
                    if (currentDelta.hasChildNodes() == true) {
                        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:") == true)
                    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:") == true)
                    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 == true) {
            value = attr1.item(i).getNodeName();
            if (value.equals("formtype") == false
                && value.equals("formpath") == false
                && value.equals("formdescription") == false
                && value.startsWith("xmlns:") == false) {
                ok = false;
                m = 0;
                while (m < l2 && ok == false) {
                    if (attr2.item(m).getNodeName().equals(value) == true) {
                        // 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 processSunletList(List        sunletList,
                                   XMLConsumer consumer,
                                   String      sunletNotAvailableMessage,
                                   long        defaultSunletTimeout)
    throws ProcessingException, SAXException, IOException, javax.xml.transform.TransformerException {
        // calling method is synced
        for(int i = 0; i < sunletList.size(); i++) {
            this.processSunlet((Object[])sunletList.get(i),
                         consumer, sunletNotAvailableMessage, defaultSunletTimeout);
        }
    }

    /**
     * Parse the fragment(tree denoted by the element)
     * and include the processed xml in the output
     */
    private void loadSunlets(Element element,
                             Map     defaultSunlets,
                             Map     mediaSunlets,
                             List    sunletList,
                             boolean parallelSunlets,
                             long    defaultSunletTimeout,
                             Element statusProfile)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        // All children, which are sunlets are processed, all other tags
        // are ignored
        if (element.hasChildNodes() == true) {
            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("sunlet") == true) {

                    // now we have a sunlet
                    this.loadSunlet((Element)current,
                             defaultSunlets,
                             mediaSunlets,
                             sunletList,
                             parallelSunlets,
                             defaultSunletTimeout,
                             statusProfile);
                }
            }
        }
    }

    /**
     * Load a sunlet and store the binary output in the list
     */
    private void loadSunlet(Element         element,
                            Map             defaultSunlets,
                            Map             mediaSunlets,
                            List            sunletList,
                            boolean         parallelSunlets,
                            long            defaultSunletTimeout,
                            Element         statusProfile)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        String sunletID = element.getAttributeNS(null, "id");

        Element sunletConf = this.getSunletConfiguration(sunletID, defaultSunlets, mediaSunlets);
        if (sunletConf != null) {

             // first: check visibility
            boolean visible = XMLUtil.getValueAsBooleanOf(element,
                "status/visible");
            // second: check media
            String media = (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_MEDIA_TYPE);
            if (visible == true && sunletConf.hasAttributeNS(null, "media") == true) {
                String sunletMedia = sunletConf.getAttributeNS(null, "media");
                visible = media.equals(sunletMedia);
            }

            if (visible == true) {

                Object[] loadedSunlet = new Object[8];
                sunletList.add(loadedSunlet);

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

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

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

                SunLetThread sunLetThread = new SunLetThread();
                Thread theThread = new Thread(sunLetThread);
                loadedSunlet[6] = sunLetThread;
                sunLetThread.init(sunletID,
                                  objectModel,
                                  this.getLogger(),
                                  response,
                                  this.getResourceConnector(),
                                  loadedSunlet,
                                  this.manager,
                                  this.resolver);
                theThread.start();
                Thread.currentThread().yield();

                if (parallelSunlets == false) {
                    sunLetThread = (SunLetThread)loadedSunlet[6];
                    if (sunLetThread != null) {
                        long startTime = System.currentTimeMillis() - ((Long)loadedSunlet[4]).longValue();
                        long timeout = ((Long)loadedSunlet[5]).longValue();
                        long waitTime;
                        if (timeout == -1) {
                            waitTime = defaultSunletTimeout;
                        } else {
                            waitTime = timeout - startTime;
                        }

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

            }

        }
    }

    /**
     * Process a sunlet which is previously loaded
     */
    private void processSunlet(Object[]    loadedSunlet,
                               XMLConsumer consumer,
                               String      notAvailableMessage,
                               long        defaultSunletTimeout)
    throws ProcessingException,
           SAXException,
           IOException,
           javax.xml.transform.TransformerException  {
        // calling method is synced

        Element sunletConf = (Element)loadedSunlet[1];
        Element element    = (Element)loadedSunlet[3];

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

            // now include all children of the sunlet element except status
            NodeList children = sunletConf.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") == false
                        && children.item(i).getNodeType() == Node.ELEMENT_NODE) {
                        IncludeXMLConsumer.includeNode(children.item(i), consumer, consumer);
                    }
                }
            }

            // now the status parameter
            SourceParameters p = XMLUtil.createParameters(XMLUtil.getFirstNodeFromPath(element, new String[] {"status"}, false), null);
            consumer.startElement("", "status", "status", attr);
            children = XMLUtil.selectNodeList(element, "status/*");
            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);

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

                while (thread != null && waitTime > 2) {
                    try {
                        Thread.sleep(15);
                        waitTime -= 15;
                    } catch(InterruptedException local) {
                        // ignore
                    }
                    thread = (SunLetThread)loadedSunlet[6];
                }
            }
            byte[] content = (byte[])loadedSunlet[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((Component)interpreter);
                    }
                }
            } else {
                notAvailableMessage = XMLUtil.getValueOf(sunletConf,
                         "configuration/messages/sunlet_not_available", notAvailableMessage);
                consumer.characters(notAvailableMessage.toCharArray(), 0, notAvailableMessage.length());
            }
            consumer.endElement("", "content", "content");
            consumer.endElement("", "sunlet", "sunlet");

        }
    }

    /**
     * Get the sunlet with the id
     */
    private Element getSunletConfiguration(String sunletID,
                                           Map    defaultSunlets,
                                           Map    mediaSunlets) {
        // calling method is synced
        String media = (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_MEDIA_TYPE);
        Map    sunlets = (Map)mediaSunlets.get(media);
        Element sunlet = null;
        if (sunlets != null) sunlet = (Element)sunlets.get(sunletID);
        if (sunlet == nullsunlet = (Element)defaultSunlets.get(sunletID);
        return sunlet;
    }

    /**
     * Get the sunLet Element
     */
    private Element getSunletElement(DocumentFragment profile,
                                     String sunletID,
                                     String sunletNr,
                                     Node[] miscNodes)
    throws ProcessingException, SAXException, 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)XMLUtil.getSingleNode(miscNodes[colindex],
                        "sunlets/sunlet[@id='"+sunletID+"' and @number='"+sunletNr+"']");
                colindex++;
            } else {
                colindex = 13;
            }
        }
        if (node == null && miscNodes[Constants.PROFILE_MISC_HEADER_CONTENT_NODE] != null) {
            node = (Element)XMLUtil.getSingleNode(miscNodes[Constants.PROFILE_MISC_HEADER_CONTENT_NODE],
                      "sunlet[@id='"+sunletID+"' and @number='"+sunletNr+"']");
        }
        if (node == null && miscNodes[Constants.PROFILE_MISC_FOOTER_CONTENT_NODE] != null) {
            node = (Element)XMLUtil.getSingleNode(miscNodes[Constants.PROFILE_MISC_FOOTER_CONTENT_NODE],
                      "sunlet[@id='"+sunletID+"' and @number='"+sunletNr+"']");
        }
        return node;
    }

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


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

            String sunletID;
            String sunletNr;
            String argument = null;

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

            // create a new sunlet: in the given column, header or footer
            if (requestString.startsWith(SunSpot.REQ_CMD_NEW) == true
                && this.isSunletAvailable(context, sunletID,
                                 (Map)theProfile.get(Constants.PROFILE_DEFAULT_SUNLETS),
                                 (Map)theProfile.get(Constants.PROFILE_MEDIA_SUNLETS))) {
                Node[] miscNodes = (Node[])theProfile.get(Constants.PROFILE_MISC_POINTER);
                // determine the sunlet number
                Node   lastSunlet = miscNodes[Constants.PROFILE_MISC_LAST_SUNLET_NODE];
                String lastNumber = null;
                if (lastSunlet != null) {
                    lastNumber = ((Element)lastSunlet).getAttributeNS(null, "number");
                    if (lastNumber != null) {
                        int value = new Integer(lastNumber).intValue();
                        value++;
                        lastNumber = ""+value;
                    }
                }
                if (lastNumber == null) lastNumber = "0";

                Node sunletsNode;
                if (sunletNr.equals("header") == true) {
                    sunletsNode = miscNodes[Constants.PROFILE_MISC_HEADER_CONTENT_NODE];
                    if (sunletsNode == null) {
                        sunletsNode = XMLUtil.selectSingleNode(profile, "profile/portal-profile/content/header");
                        miscNodes[Constants.PROFILE_MISC_HEADER_CONTENT_NODE] = sunletsNode;
                    } else { // remove old sunlet
                        Node oldSunlet = XMLUtil.getFirstNodeFromPath(sunletsNode, new String[] {"sunlet"}, false);
                        if (oldSunlet != null) sunletsNode.removeChild(oldSunlet);
                    }
                } else if (sunletNr.equals("footer") == true) {
                    sunletsNode = miscNodes[Constants.PROFILE_MISC_FOOTER_CONTENT_NODE];
                    if (sunletsNode == null) {
                        sunletsNode = XMLUtil.selectSingleNode(profile, "profile/portal-profile/content/footer");
                        miscNodes[Constants.PROFILE_MISC_FOOTER_CONTENT_NODE] = sunletsNode;
                    } else { // remove old sunlet
                        Node oldSunlet = XMLUtil.getFirstNodeFromPath(sunletsNode, new String[] {"sunlet"}, false);
                        if (oldSunlet != null) sunletsNode.removeChild(oldSunlet);
                    }
                } else {
                    Node columnNode = miscNodes[7+new Integer(sunletNr).intValue()];
                    sunletsNode = XMLUtil.getFirstNodeFromPath(columnNode, new String[] {"sunlets"}, false);
                }
                Element sunletNode;
                Document doc = sunletsNode.getOwnerDocument();
                sunletNode = doc.createElementNS(null, "sunlet");
                sunletsNode.appendChild(sunletNode);
                sunletNode.setAttributeNS(null, "id", sunletID);
                sunletNode.setAttributeNS(null, "number", lastNumber);
                // set position
                NodeList childs = XMLUtil.getNodeListFromPath(sunletsNode, new String[] {"sunlet"});
                int childsCount = (childs == null ? 0 : childs.getLength());
                sunletNode.setAttributeNS(null, "position", ""+(childsCount));
                miscNodes[Constants.PROFILE_MISC_LAST_SUNLET_NODE] = sunletNode;

                // copy status
                Element configElement = this.getSunletConfiguration(sunletID,
                                                      (Map)theProfile.get(Constants.PROFILE_DEFAULT_SUNLETS),
                                                      (Map)theProfile.get(Constants.PROFILE_MEDIA_SUNLETS));
                Element configStatus = (Element)XMLUtil.getFirstNodeFromPath(configElement, new String[] {"status"}, false);
                sunletNode.appendChild(configStatus.cloneNode(true));

                // clear type information for each status
                Element status = (Element)sunletNode.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") == true)
                                statusNode.removeAttributeNS(null, "formpath");
                            if (statusNode.hasAttributeNS(null, "formtype") == true)
                                statusNode.removeAttributeNS(null, "formtype");
                            if (statusNode.hasAttributeNS(null, "formdescription") == true)
                                statusNode.removeAttributeNS(null, "formdescription");
                        }
                    }
                }
                result = true;

           } else {
                sunlet = this.getSunletElement(profile,
                                   sunletID,
                                    sunletNr,
                                    (Node[])theProfile.get(Constants.PROFILE_MISC_POINTER));
                if (sunlet != null) {
                    if (requestString.startsWith(SunSpot.REQ_CMD_CLOSE) == true ||
                        requestString.startsWith(SunSpot.REQ_CMD_HIDE) == true) {
                         Node node = XMLUtil.selectSingleNode(sunlet, "status/visible");
                         XMLUtil.setValueOfNode(node, "false");
                    } else if (requestString.startsWith(SunSpot.REQ_CMD_OPEN) == true ||
                        requestString.startsWith(SunSpot.REQ_CMD_SHOW) == true) {
                         Node node = XMLUtil.selectSingleNode(sunlet, "status/visible");
                         XMLUtil.setValueOfNode(node, "true");
                    } else if (requestString.startsWith(SunSpot.REQ_CMD_MINIMIZE) == true) {
                         Node node = XMLUtil.selectSingleNode(sunlet, "status/size");
                         XMLUtil.setValueOfNode(node, "min");
                    } else if (requestString.startsWith(SunSpot.REQ_CMD_MAXIMIZE) == true) {
                         Node node = XMLUtil.selectSingleNode(sunlet, "status/size");
                         XMLUtil.setValueOfNode(node, "max");
                    } else if (requestString.startsWith(SunSpot.REQ_CMD_CUSTOMIZE) == true) {
                         Node node = XMLUtil.selectSingleNode(sunlet, "status/customize");
                         XMLUtil.setValueOfNode(node, "true");
                    } else if (requestString.startsWith(SunSpot.REQ_CMD_UPDATE) == true) {
                         Node node = XMLUtil.selectSingleNode(sunlet, "status/customize");
                         XMLUtil.setValueOfNode(node, "false");
                    } else if (requestString.startsWith(SunSpot.REQ_CMD_DELETE) == true) {
                        // delete the status of the sunlet
                        Node statusNode = XMLUtil.getSingleNode(profile,
                             "profile/status-profile/customization/sunlet[@id='"+sunletID+"' and @number='"+sunletNr+"']");
                        if (statusNode != null) {
                            statusNode.getParentNode().removeChild(statusNode);
                            Element configElement = this.getSunletConfiguration(sunletID,
                                                      (Map)theProfile.get(Constants.PROFILE_DEFAULT_SUNLETS),
                                                      (Map)theProfile.get(Constants.PROFILE_MEDIA_SUNLETS));
                            boolean isPersistent = XMLUtil.getValueAsBooleanOf(configElement, "configuration/persistent", false);
                            if (isPersistent == true) {
                                // mark the status profile to be saved
                                theProfile.put(Constants.PROFILE_SAVE_STATUS_FLAG, "true");
                            }
                        }
                        String posAttr = sunlet.getAttributeNS(null, "position");
                        NodeList followUps = XMLUtil.selectNodeList(sunlet.getParentNode(), "sunlet[@position > '"+posAttr+"']");
                        sunlet.getParentNode().removeChild(sunlet);
                        sunlet = 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(SunSpot.REQ_CMD_MOVE) == true) {
                        if (argument != null) {
                            Element  sunletsElement = (Element)XMLUtil.getSingleNode(profile,
                                  "profile/portal-profile/content/column[@position='"+argument+"']/sunlets");
                            if (sunletsElement != null) {
                                if (sunlet.getParentNode().equals(sunletsElement) == false) {
                                     String posAttr = sunlet.getAttributeNS(null, "position");
                                     NodeList followUps = XMLUtil.selectNodeList(sunlet.getParentNode(), "sunlet[@position > '"+posAttr+"']");
                                     sunlet.getParentNode().removeChild(sunlet);
                                     // set position attribute
                                     NodeList childs = XMLUtil.getNodeListFromPath(sunletsElement, new String[] {"sunlet"});
                                     int childsCount = (childs == null ? 0 : childs.getLength());
                                     sunlet.setAttributeNS(null, "position", "" + (childsCount + 1));
                                     sunletsElement.appendChild(sunlet);
                                     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(SunSpot.REQ_CMD_MOVEROW) == true) {
                        if (argument != null) {
                            Element newSunlet = (Element)XMLUtil.getSingleNode(sunlet.getParentNode(),
                                                 "sunlet[@position='"+argument+"']");
                            if (newSunlet != null) {
                                String position = sunlet.getAttributeNS(null, "position");
                                sunlet.removeAttributeNS(null, "position");
                                sunlet.setAttributeNS(null, "position", argument);
                                newSunlet.removeAttributeNS(null, "position");
                                newSunlet.setAttributeNS(null, "position", position);
                            }
                        }
                    }
                }
            }
        }

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

    /**
     * Check if the sunlet is available for the current logged in user
     * If the user is not logged in, this returns false.
     * First the default sunlets are searched. If none is found then
     * the sunlets for each media are searched.
     */
    private boolean isSunletAvailable(SessionContext context,
                                    String sunletID,
                                    Map defaultSunlets,
                                    Map mediaSunlets)
    throws ProcessingException {
        // no sync required
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN isSunletAvailable sunlet="+sunletID);
        }
        boolean result = false;

        if (context != null) {
            result = defaultSunlets.containsKey(sunletID);
            if (result == false) {
                Iterator iter = mediaSunlets.values().iterator();
                while (result == false && iter.hasNext() == true) {
                    result = ((Map)iter.next()).containsKey(sunletID);
                }
            }
        }

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

    /**
     * Check the authentication for the sunlet. If it is not available do a redirect
     */
    public boolean checkAuthentication(Redirector redirector, String sunletID)
    throws SAXException, IOException, ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN checkAuthentication sunlet="+sunletID);
        }
        boolean result = false;
        SessionContext context = this.getContext(false);
        if (context != null
            && (String)context.getAttribute(SunSpot.ATTRIBUTE_PORTAL_ROLE) != null) {

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

                if (theProfile != null) {
                    if (sunletID == null || sunletID.trim().length() == 0) {
                        result = true;
                    } else {
                        result = this.isSunletAvailable(context,
                                      sunletID,
                                      (Map)theProfile.get(Constants.PROFILE_DEFAULT_SUNLETS),
                                      (Map)theProfile.get(Constants.PROFILE_MEDIA_SUNLETS));
                    }
                }
            } finally {
                this.getSunShineComponent().stopReadingTransaction(context);
            } // end synced
        }


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

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

    /**
     * Get the configuration. This configuration is an sunRise application configuration
     * for the current application with the name "sunSpot".
     * The first time this configuration is build it is stored in the session
     * so later requests get the cached result.
     */
    private Map getConfiguration()
    throws SAXException, IOException, ProcessingException {
        // synchronized
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN getConfiguration");
        }
        Map result = null;
        String appName = (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME);
        String handlerName = (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME);
        Session session = this.getSunShineComponent().getSession(false);
        if (session != null && appName != null && handlerName != null) {

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

                    try {
                        Configuration config;

                        Configuration conf = this.getSunRise().getModuleConfiguration(Constants.SUNRISE_MODULE_NAME);
                        if (conf == null) {
                            throw new ProcessingException("sunSpot: 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(Constants.CONF_AUTH_REDIRECT, config.getValue());
                        }

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

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

                        // parallel sunlets
                        config = conf.getChild("process-sunlets-parallel", false);
                        if (config != null) {
                            result.put(Constants.CONF_PARALLEL_SUNLETS, new Boolean(config.getValueAsBoolean(false)));
                        } else {
                            result.put(Constants.CONF_PARALLEL_SUNLETS, new Boolean(false));
                        }

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

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

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

                        // base resource, type is optional
                        child = config.getChild("layout-base", false);
                        if (child == null) {
                            throw new ProcessingException("sunSpot: layout-base required for application '" + appName + "'");
                        }
                        result.put(Constants.CONF_LAYOUTBASE_RESOURCE, new Resource(this.resolver,
                                                                                    child.getAttribute("uri")));
                        child = config.getChild("sunlet-base", false);
                        if (child == null) {
                            throw new ProcessingException("sunSpot: sunlet-base required for application '" + appName + "'");
                        }
                        result.put(Constants.CONF_SUNLETBASE_RESOURCE, new Resource(this.resolver,
                                                                                    child.getAttribute("uri")));
                        child = config.getChild("type-base", false);
                        if (child != null) {
                            result.put(Constants.CONF_TYPEBASE_RESOURCE, new Resource(this.resolver,
                                                                                      child.getAttribute("uri")));
                        }

                        // sunlet base save (is optional)
                        child = config.getChild("sunlet-base-save", false);
                        if (child != null) {
                            result.put(Constants.CONF_SUNLETBASE_SAVE_RESOURCE, new Resource(this.resolver,
                                                                                             child.getAttribute("uri")));
                        }

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

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

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

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

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

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

        }

        if (this.getLogger().isDebugEnabled() == true) {
            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() == true) {
            this.getLogger().debug("BEGIN createProfile context="+context+
                                   ", type="+type+
                                   ", role="+role+
                                   ", id="+id);
        }

        SourceParameters pars = this.getSunRise().createParameters(null);
        pars.setSingleParameterValue("type", type);
        pars.setSingleParameterValue("admin", (adminProfile == true ? "true" : "false"));

        if (type.equals(SunSpot.BUILDTYPE_VALUE_ID) == false ||
            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("sunSpot Configuration not found.");
        }

        // is the configuration build by using a own resource?
        Resource resource = (Resource)map.get(Constants.CONF_BUILD_RESOURCE);
        if (resource != null) {
            if (this.getLogger().isInfoEnabled() == true) {
                this.getLogger().info("Building sunSpot profile: " + resource.getResourceIdentifier());
            }
            this.getResourceConnector().loadXML(resource.getResourceType(),null,
                                       resource.getResourceIdentifier(),pars);
        } else {
            this.buildProfile(type, role, id, adminProfile);
        }

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

    }

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

        SourceParameters pars = new SourceParameters();
        pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
        pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));
        pars.setSingleParameterValue("profile", "sunlet-base");

        // First load the base profiles: sunletProfile + layoutProfile
        res = (Resource)config.get(Constants.CONF_SUNLETBASE_RESOURCE);
        if (res == null) {
            throw new ProcessingException("No configuration for sunSpot-sunlet base profile found.");
        }
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("Loading sunLet base profile");
        }
        sunletsFragment = this.loadXML(res.getResourceType(), null,
                                       res.getResourceIdentifier(), pars,
                                       "Error loading sunLet base profile." + res.getResourceIdentifier());
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("sunLet base profile loaded");
        }
        res = (Resource)config.get(Constants.CONF_LAYOUTBASE_RESOURCE);
        if (res == null) {
            throw new ProcessingException("No configuration for sunSpot-layout base profile found.");
        }
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("loading layout base profile");
        }
        pars.setSingleParameterValue("profile", "layout-base");
        layoutFragment = this.loadXML(res.getResourceType(), null,
                                      res.getResourceIdentifier(), pars,
                                      "Error loading layout base profile " + res.getResourceIdentifier());
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("layout base profile loaded");
        }
        // now create the base profile containing the above profiles
        profileDoc = XMLUtil.createDocument();
        profile = profileDoc.createDocumentFragment();
        profileRoot = profileDoc.createElementNS(null, "profile");
        profile.appendChild(profileRoot);
        profileRoot.appendChild(profileDoc.importNode(XMLUtil.selectSingleNode(layoutFragment,
                                                                  "layout-profile"), true));
        profileRoot.appendChild(profileDoc.importNode(XMLUtil.selectSingleNode(sunletsFragment,
                                                                  "sunlets-profile"), true));

        // if avalailable append the type profile
        if (adminProfile == true) {
            res = (Resource)config.get(Constants.CONF_ADMIN_TYPE_BASE);
            pars.setSingleParameterValue("profile", "admin-type-base");
        } else {
            res = (Resource)config.get(Constants.CONF_TYPEBASE_RESOURCE);
            pars.setSingleParameterValue("profile", "type-base");
        }
        if (res != null) {
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("loading type base profile");
            }
            typeFragment = this.loadXML(res.getResourceType(), null,
                                        res.getResourceIdentifier(), pars,
                                        "Error loading type base profile " +res.getResourceIdentifier());
            profileRoot.appendChild(profileDoc.importNode(XMLUtil.selectSingleNode(typeFragment,
                              "type-profile"), true));

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

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

    /**
     * Build the global profile.
     */
    private void buildGlobalProfile(Element profileRoot,
                                    Map config,
                                    boolean adminProfile)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("BEGIN buildGlobalProfile profileRoot="+profileRoot+", config="+config+", adminProfile="+adminProfile);
        }
        DocumentFragment globalFragment;
        Resource res = (Resource)config.get(Constants.CONF_GLOBALDELTA_LOADRESOURCE);
        if (res == null) {
            throw new ProcessingException("No configuration for sunSpot-role delta profile found.");
        }
        SourceParameters pars = new SourceParameters();
        pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
        pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));
        pars.setSingleParameterValue("profile", "global-delta");

        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("loading global profile");
        }
        globalFragment = this.loadXML(res.getResourceType(), null,
                                      res.getResourceIdentifier(), pars,
                                      "Error loading global profile " + res.getResourceIdentifier());
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("global profile loaded");
        }
        this.importProfileDelta(profileRoot, globalFragment, "global-delta", "layout-delta");
        this.importProfileDelta(profileRoot, globalFragment, "global-delta", "sunlets-delta");
        this.addProfilePart(profileRoot, globalFragment, "global-delta", "portal-profile");
        this.addProfilePart(profileRoot, globalFragment, "global-delta", "personal-profile");

        // types
        res = (Resource)config.get(Constants.CONF_GLOBALDELTA_TYPERESOURCE);
        if (adminProfile == false && res != null) {
            pars.setSingleParameterValue("profile", "global-type-delta");
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("loading global type profile");
            }
            globalFragment = this.loadXML(res.getResourceType(), null,
                                          res.getResourceIdentifier(), pars,
                                          "Error loading global type profile " + res.getResourceIdentifier());
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("global type profile loaded");
            }
            this.addProfilePart(profileRoot, globalFragment, "global-delta", "type-profile");
        }
        if (this.getLogger().isDebugEnabled() == true) {
            this.getLogger().debug("END buildGlobalProfile");
        }
    }

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

        DocumentFragment roleFragment;
        SourceParameters pars;
        pars = new SourceParameters();
        pars.setSingleParameterValue("role", role);
        pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
        pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));
        pars.setSingleParameterValue("profile", "role-delta");

        Resource res = (Resource)config.get(Constants.CONF_ROLEDELTA_LOADRESOURCE);
        if (res != null) {
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("loading role profile");
            }
            roleFragment = this.loadXML(res.getResourceType(), null,
                                        res.getResourceIdentifier(), pars,
                                        "Error loading role profile "  + res.getResourceIdentifier());
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("role profile loaded");
            }
            this.importProfileDelta(profileRoot, roleFragment, "role-delta", "layout-delta");
            this.importProfileDelta(profileRoot, roleFragment, "role-delta", "sunlets-delta");
            this.addProfilePart(profileRoot, roleFragment, "role-delta", "portal-profile");
            this.importProfileDelta(profileRoot, roleFragment, "role-delta", "personal-delta");
        }

        // types
        res = (Resource)config.get(Constants.CONF_ROLEDELTA_TYPERESOURCE);
        if (adminProfile == false && res != null) {
            pars.setSingleParameterValue("profile", "role-type-delta");
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("loading role type profile");
            }
            roleFragment = this.loadXML(res.getResourceType(), null,
                                        res.getResourceIdentifier(), pars,
                                        "Error loading role type profile " + res.getResourceIdentifier());
            if (this.getLogger().isDebugEnabled() == true) {
                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, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        DocumentFragment userFragment;
        SourceParameters pars;
        pars = new SourceParameters();
        pars.setSingleParameterValue("ID", id);
        pars.setSingleParameterValue("role", role);
        pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
        pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));
        pars.setSingleParameterValue("profile", "user-delta");

        Resource res = (Resource)config.get(Constants.CONF_USERDELTA_LOADRESOURCE);
        if (res != null) {
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("loading user profile");
            }
            userFragment = this.loadXML(res.getResourceType(), null,
                                        res.getResourceIdentifier(), pars,
                                        "Error loading user profile " + res.getResourceIdentifier());
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("user profile loaded");
            }
            this.importProfileDelta(profileRoot, userFragment, "user-delta", "layout-delta");
            this.importProfileDelta(profileRoot, userFragment, "user-delta", "sunlets-delta");
            this.addProfilePart(profileRoot, userFragment, "user-delta", "portal-profile");
            this.importProfileDelta(profileRoot, userFragment, "user-delta", "personal-delta");
        }

        // types
        res = (Resource)config.get(Constants.CONF_USERDELTA_TYPERESOURCE);
        if (adminProfile == false && res != null) {
            pars.setSingleParameterValue("profile", "user-type-delta");
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("loading user type profile");
            }
            userFragment = this.loadXML(res.getResourceType(), null,
                                        res.getResourceIdentifier(), pars,
                                        "Error loading user type profile " + res.getResourceIdentifier());
            if (this.getLogger().isDebugEnabled() == true) {
                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,
                                        boolean adminProfile)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        Resource res = (Resource)config.get(Constants.CONF_STATUS_LOADRESOURCE);

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

        if (res != null) {
            DocumentFragment userFragment;
            SourceParameters pars;
            pars = new SourceParameters();
            pars.setSingleParameterValue("ID", id);
            pars.setSingleParameterValue("role", role);
            pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
            pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));
            pars.setSingleParameterValue("profile", "user-status");
            if (this.getLogger().isDebugEnabled() == true) {
                this.getLogger().debug("loading user status profile");
            }
            userFragment = this.loadXML(res.getResourceType(), null,
                                        res.getResourceIdentifier(), pars,
                                        "Error loading user status profile " + res.getResourceIdentifier());
            if (this.getLogger().isDebugEnabled() == true) {
                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 (XMLUtil.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,
                                       boolean adminProfile)
    throws ProcessingException, SAXException, javax.xml.transform.TransformerException {
        // calling method is synced
        Resource res = (Resource)config.get(Constants.CONF_STATUS_SAVERESOURCE);
        Element statusProfile = (Element)XMLUtil.getFirstNodeFromPath((DocumentFragment)profile.get(Constants.PROFILE_PROFILE),
                         new String[] {"profile","status-profile"}, false);
        if (res != null && statusProfile != null) {
            DocumentFragment userFragment = statusProfile.getOwnerDocument().createDocumentFragment();
            Element oldParent = (Element)statusProfile.getParentNode();
            Element saveStatus = (Element)statusProfile.cloneNode(true);
            userFragment.appendChild(saveStatus);
            // now filter all not persistent sunlets!
            NodeList list = XMLUtil.getNodeListFromPath(saveStatus, new String[] {"customization","sunlet"});
            String sunletID;
            String sunletNumber;
            Element sunlet;
            Element sunletConfig;
            Map sunletConfigs = (Map)profile.get(Constants.PROFILE_DEFAULT_SUNLETS);
            Map mediaSunletConfigs = (Map)profile.get(Constants.PROFILE_MEDIA_SUNLETS);
            boolean isPersistent;
            for(int i = 0; i < list.getLength(); i++) {
                sunlet = (Element)list.item(i);
                sunletID = sunlet.getAttributeNS(null, "id");
                sunletNumber = sunlet.getAttributeNS(null, "number");
                sunletConfig = this.getSunletConfiguration(sunletID, sunletConfigs, mediaSunletConfigs);
                isPersistent = XMLUtil.getValueAsBooleanOf(sunletConfig, "configuration/persistent", false);
                if (isPersistent == false) {
                    sunlet.getParentNode().removeChild(sunlet);
                }
            }

            try {

                SourceParameters pars;
                pars = new SourceParameters();
                pars.setSingleParameterValue("ID", id);
                pars.setSingleParameterValue("role", role);
                pars.setSingleParameterValue("application", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
                pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));
                pars.setSingleParameterValue("profile", "user-status");

                this.getResourceConnector().saveXML(res.getResourceType(), null,
                              res.getResourceIdentifier(), pars,
                              userFragment);
            } finally {
                userFragment.removeChild(saveStatus);
            }
        }
    }

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

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

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

                        // first iteration: all changing commands
                        while (enum.hasMoreElements() == true) {
                            current = (String)enum.nextElement();
                            if (current.startsWith(SunSpot.REQ_PARAMETER_CONF) == true) {
                                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(Constants.PROFILE_TYPE_CONF_PATHS);
                                    String path = (String)typePaths.get(pathIndex);
                                    if (path != null) {
                                        NodeList nodes = XMLUtil.selectNodeList(profile, path);
                                        if (nodes != null) {
                                            Node node = nodes.item(place);
                                            if (node != null) {
                                                if (node.equals(columns) == false) {
                                                        XMLUtil.setValueOfNode(node, request.getParameter(current));
                                                }
                                            }
                                        }

                                    }
                                }
                            }
                        }

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

                            current = (String)enum.nextElement();
                            if (current.startsWith(SunSpot.REQ_PARAMETER_CONF) == true) {
                                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(Constants.PROFILE_TYPE_CONF_PATHS);
                                    String path = (String)typePaths.get(pathIndex);
                                    if (path != null) {
                                        NodeList nodes = XMLUtil.selectNodeList(profile, path);
                                        if (nodes != null) {
                                            Node node = nodes.item(place);
                                            if (node != null) {
                                                if (node.equals(columns) == true) {
                                                    int columnNumber = new Integer(this.request.getParameter(current)).intValue();
                                                    int oldNumber = new Integer(XMLUtil.getValueOfNode(columns)).intValue();
                                                    if (columnNumber > 0 && columnNumber != oldNumber && columnNumber <= Constants.MAX_COLUMNS) {
                                                        this.changeColumns(profile,
                                                               oldNumber,
                                                               columnNumber,
                                                               miscNodes);
                                                        calculate = true;
                                                        XMLUtil.setValueOfNode(node, request.getParameter(current));
                                                    }
                                                }
                                            }
                                        }

                                    }
                                }

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

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

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

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

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

                            // build delta
                            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", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_APPLICATION_NAME));
                            pars.setSingleParameterValue("handler", (String)this.request.getAttribute(org.apache.cocoon.sunshine.sunrise.Constants.REQUEST_ATTRIBUTE_HANDLER_NAME));
                            this.getResourceConnector().saveXML(saveResource.getResourceType(), null,
                                 saveResource.getResourceIdentifier(), pars,
                                 delta);
                            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) == true) {
                            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() == true) {
            this.getLogger().debug("END changeProfile");
        }
    }

    /**
     * Change the number of the columns
     */
    private void changeColumns(DocumentFragment profile,
                               int oldNumber,
                               int columnNumber,
                               Node[] miscNodes)
    throws SAXException, javax.xml.transform.TransformerException {
        // calling method is (hopefully) synced
        if (columnNumber < oldNumber) {
            // remove columns and all sunlets to the first one
            Node columnNode;
            Node firstColumn = XMLUtil.getSingleNode(profile,
                        "profile/portal-profile/content/column[@position='1']/sunlets");
            NodeList firstColumnSunlets = XMLUtil.getNodeListFromPath(firstColumn, new String[] {"sunlet"});
            int sunletsCount = (firstColumnSunlets == null ? 0 : firstColumnSunlets.getLength());
            for(int i = columnNumber + 1; i <= oldNumber; i++) {
                columnNode = miscNodes[7+i];
                if (columnNode != null) {
                    NodeList sunlets = XMLUtil.getNodeListFromPath(columnNode, new String[] {"sunlets","sunlet"});
                    Node sunlet;
                    if (sunlets != null && sunlets.getLength() > 0) {
                        for(int m = 0; m < sunlets.getLength(); m++) {
                            sunlet = sunlets.item(m);
                            sunlet.getParentNode().removeChild(sunlet);
                            sunletsCount++;
                            ((Element)sunlet).setAttributeNS(null, "position", "" + sunletsCount);
                            firstColumn.appendChild(sunlet);
                        }
                    }
                    columnNode.getParentNode().removeChild(columnNode);
                    miscNodes[7+i] = null;
                }
            }
        } else if (columnNumber <= Constants.MAX_COLUMNS) {
            // add new columns
            Node contentNode = XMLUtil.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, "sunlets");
                newColumn.appendChild(el);
            }
        }
    }

    /** Empty attributes (for performance)
     */
    private Attributes emptyAttributes = new AttributesImpl();

    /**
     * Send SAX events to the next pipeline component.
     * The characters event for the given text is send to the next
     * component in the current pipeline.
     * @param text The string containing the information.
     */
    public void sendTextEvent(XMLConsumer consumer, String text)
    throws SAXException {
        consumer.characters(text.toCharArray(), 0, text.length());
    }

    /**
     * Send SAX events to the next pipeline component.
     * The startElement event for the given element is send
     * to the next component in the current pipeline.
     * The element has no namespace and no attributes
     * @param localname The name of the event.
     */
    public void sendStartElementEvent(XMLConsumer consumer, String localname)
    throws SAXException {
        consumer.startElement("", localname, localname, emptyAttributes);
    }

    /**
     * Send SAX events to the next pipeline component.
     * The startElement event for the given element is send
     * to the next component in the current pipeline.
     * The element has no namespace.
     * @param localname The name of the event.
     * @param attr The Attributes of the element
     */
    public void sendStartElementEvent(XMLConsumer consumer, String localname, Attributes attr)
    throws SAXException {
        consumer.startElement("", localname, localname, attr);
    }

    /**
     * Send SAX events to the next pipeline component.
     * The endElement event for the given element is send
     * to the next component in the current pipeline.
     * The element has no namespace.
     * @param localname The name of the event.
     */
    public void sendEndElementEvent(XMLConsumer consumer, String localname)
    throws SAXException {
        consumer.endElement("", localname, localname);
    }

    /**
     * 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.
     */
    public void sendEvents(XMLConsumer consumer, Node node)
    throws SAXException {
        IncludeXMLConsumer.includeNode(node, consumer, consumer);
    }

}
TOP

Related Classes of org.apache.cocoon.sunshine.sunspot.SunSpot

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.