org.gatein.wsrp.producer.handlers.PortletManagementHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.gatein.wsrp.producer.handlers.PortletManagementHandler.java

Source

/*
 * JBoss, a division of Red Hat
 * Copyright 2011, Red Hat Middleware, LLC, and individual
 * contributors as indicated by the @authors tag. See the
 * copyright.txt in the distribution for a full listing of
 * individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.gatein.wsrp.producer.handlers;

import com.google.common.base.Function;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import org.gatein.common.i18n.LocalizedString;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.common.util.ParameterValidation;
import org.gatein.exports.ExportManager;
import org.gatein.exports.data.ExportContext;
import org.gatein.exports.data.ExportPortletData;
import org.gatein.pc.api.InvalidPortletIdException;
import org.gatein.pc.api.NoSuchPortletException;
import org.gatein.pc.api.Portlet;
import org.gatein.pc.api.PortletInvokerException;
import org.gatein.pc.api.PortletStateType;
import org.gatein.pc.api.info.PortletInfo;
import org.gatein.pc.api.info.PreferenceInfo;
import org.gatein.pc.api.info.PreferencesInfo;
import org.gatein.pc.api.state.DestroyCloneFailure;
import org.gatein.pc.api.state.PropertyChange;
import org.gatein.pc.api.state.PropertyMap;
import org.gatein.registration.Registration;
import org.gatein.registration.RegistrationLocal;
import org.gatein.wsrp.WSRPConstants;
import org.gatein.wsrp.WSRPExceptionFactory;
import org.gatein.wsrp.WSRPTypeFactory;
import org.gatein.wsrp.WSRPUtils;
import org.gatein.wsrp.producer.PortletManagementInterface;
import org.gatein.wsrp.producer.Utils;
import org.gatein.wsrp.producer.WSRPProducerImpl;
import org.gatein.wsrp.spec.v2.ErrorCodes;
import org.gatein.wsrp.spec.v2.WSRP2ExceptionFactory;
import org.oasis.wsrp.v2.AccessDenied;
import org.oasis.wsrp.v2.ClonePortlet;
import org.oasis.wsrp.v2.CopiedPortlet;
import org.oasis.wsrp.v2.CopyPortlets;
import org.oasis.wsrp.v2.CopyPortletsResponse;
import org.oasis.wsrp.v2.DestroyPortlets;
import org.oasis.wsrp.v2.DestroyPortletsResponse;
import org.oasis.wsrp.v2.ExportByValueNotSupported;
import org.oasis.wsrp.v2.ExportPortlets;
import org.oasis.wsrp.v2.ExportPortletsResponse;
import org.oasis.wsrp.v2.ExportedPortlet;
import org.oasis.wsrp.v2.Extension;
import org.oasis.wsrp.v2.FailedPortlets;
import org.oasis.wsrp.v2.GetPortletDescription;
import org.oasis.wsrp.v2.GetPortletProperties;
import org.oasis.wsrp.v2.GetPortletPropertyDescription;
import org.oasis.wsrp.v2.GetPortletsLifetime;
import org.oasis.wsrp.v2.GetPortletsLifetimeResponse;
import org.oasis.wsrp.v2.ImportPortlet;
import org.oasis.wsrp.v2.ImportPortlets;
import org.oasis.wsrp.v2.ImportPortletsFailed;
import org.oasis.wsrp.v2.ImportPortletsResponse;
import org.oasis.wsrp.v2.ImportedPortlet;
import org.oasis.wsrp.v2.InconsistentParameters;
import org.oasis.wsrp.v2.InvalidHandle;
import org.oasis.wsrp.v2.InvalidRegistration;
import org.oasis.wsrp.v2.InvalidUserCategory;
import org.oasis.wsrp.v2.Lifetime;
import org.oasis.wsrp.v2.MissingParameters;
import org.oasis.wsrp.v2.ModifyRegistrationRequired;
import org.oasis.wsrp.v2.OperationFailed;
import org.oasis.wsrp.v2.OperationNotSupported;
import org.oasis.wsrp.v2.PortletContext;
import org.oasis.wsrp.v2.PortletDescription;
import org.oasis.wsrp.v2.PortletDescriptionResponse;
import org.oasis.wsrp.v2.PortletPropertyDescriptionResponse;
import org.oasis.wsrp.v2.Property;
import org.oasis.wsrp.v2.PropertyDescription;
import org.oasis.wsrp.v2.PropertyList;
import org.oasis.wsrp.v2.RegistrationContext;
import org.oasis.wsrp.v2.ReleaseExport;
import org.oasis.wsrp.v2.ResetProperty;
import org.oasis.wsrp.v2.ResourceList;
import org.oasis.wsrp.v2.ResourceSuspended;
import org.oasis.wsrp.v2.SetExportLifetime;
import org.oasis.wsrp.v2.SetPortletProperties;
import org.oasis.wsrp.v2.SetPortletsLifetime;
import org.oasis.wsrp.v2.SetPortletsLifetimeResponse;
import org.oasis.wsrp.v2.UserContext;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Handles PortletManagement calls on behalf of the producer.
 *
 * @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
 * @version $Revision: 11147 $
 * @since 2.4
 */
public class PortletManagementHandler extends ServiceHandler implements PortletManagementInterface {
    private static final String GET_PORTLET_PROPERTY_DESCRIPTION = "GetPortletPropertyDescription";
    private static final String GET_PORTLET_PROPERTIES = "GetPortletProperties";
    private static final String PORTLET_CONTEXT = "PortletContext";
    private static final String GET_PORTLET_DESCRIPTION = "GetPortletDescription";

    private static final Logger log = LoggerFactory.getLogger(PortletManagementHandler.class);

    public PortletManagementHandler(WSRPProducerImpl producer) {
        super(producer);
    }

    public PortletDescriptionResponse getPortletDescription(GetPortletDescription getPortletDescription)
            throws AccessDenied, InconsistentParameters, InvalidHandle, InvalidRegistration, InvalidUserCategory,
            MissingParameters, ModifyRegistrationRequired, OperationFailed, OperationNotSupported,
            ResourceSuspended {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(getPortletDescription, GET_PORTLET_DESCRIPTION);
        Registration registration = producer
                .getRegistrationOrFailIfInvalid(getPortletDescription.getRegistrationContext());

        PortletContext portletContext = getPortletDescription.getPortletContext();
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(portletContext, PORTLET_CONTEXT,
                GET_PORTLET_DESCRIPTION);

        UserContext userContext = getPortletDescription.getUserContext();
        checkUserAuthorization(userContext);

        // RegistrationLocal.setRegistration is called further down the invocation in ServiceDescriptionHandler.getPortletDescription
        final List<String> desiredLocales = WSRPUtils
                .replaceByEmptyListIfNeeded(getPortletDescription.getDesiredLocales());
        PortletDescription description = producer.getPortletDescription(portletContext, desiredLocales,
                registration);
        return WSRPTypeFactory.createPortletDescriptionResponse(description);
    }

    public PortletPropertyDescriptionResponse getPortletPropertyDescription(
            GetPortletPropertyDescription getPortletPropertyDescription) throws AccessDenied,
            InconsistentParameters, InvalidHandle, InvalidRegistration, InvalidUserCategory, MissingParameters,
            ModifyRegistrationRequired, OperationFailed, OperationNotSupported, ResourceSuspended {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(getPortletPropertyDescription,
                GET_PORTLET_PROPERTY_DESCRIPTION);

        PortletContext portletContext = getPortletPropertyDescription.getPortletContext();
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(portletContext, PORTLET_CONTEXT,
                GET_PORTLET_PROPERTY_DESCRIPTION);

        try {
            Registration registration = producer
                    .getRegistrationOrFailIfInvalid(getPortletPropertyDescription.getRegistrationContext());
            RegistrationLocal.setRegistration(registration);

            UserContext userContext = getPortletPropertyDescription.getUserContext();
            checkUserAuthorization(userContext);

            Portlet portlet = getPortletFrom(portletContext, registration);
            PortletInfo info = portlet.getInfo();
            PreferencesInfo prefsInfo = info.getPreferences();

            List<PropertyDescription> descs = Collections.emptyList();
            if (prefsInfo != null) {
                Set keySet = prefsInfo.getKeys();
                descs = new ArrayList<PropertyDescription>(keySet.size());
                for (Object key : keySet) {
                    PreferenceInfo prefInfo = prefsInfo.getPreference((String) key);

                    // WSRP Spec 8.7: return only the portion of the Portlet's persistent state the user is allowed to modify
                    // if read only status is not determined, we consider it as being read-only to be safe
                    Boolean readOnly = prefInfo.isReadOnly();
                    if (readOnly != null && !readOnly) {
                        //todo: check what we should use key
                        //todo: right now we only support String properties
                        List<String> desiredLocales = getPortletPropertyDescription.getDesiredLocales();
                        desiredLocales = WSRPUtils.replaceByEmptyListIfNeeded(desiredLocales);
                        PropertyDescription desc = WSRPTypeFactory.createPropertyDescription(prefInfo.getKey(),
                                WSRPConstants.XSD_STRING);
                        desc.setLabel(
                                Utils.convertToWSRPLocalizedString(prefInfo.getDisplayName(), desiredLocales));
                        desc.setHint(Utils.convertToWSRPLocalizedString(prefInfo.getDescription(), desiredLocales));
                        descs.add(desc);
                    }
                }
            }

            return WSRPTypeFactory.createPortletPropertyDescriptionResponse(descs);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    public PortletContext clonePortlet(ClonePortlet clonePortlet) throws AccessDenied, InconsistentParameters,
            InvalidHandle, InvalidRegistration, InvalidUserCategory, MissingParameters, ModifyRegistrationRequired,
            OperationFailed, OperationNotSupported, ResourceSuspended {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(clonePortlet, "ClonePortlet");

        PortletContext portletContext = clonePortlet.getPortletContext();
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(portletContext, "PortletContext",
                "ClonePortlet");

        Registration registration = producer.getRegistrationOrFailIfInvalid(clonePortlet.getRegistrationContext());

        UserContext userContext = clonePortlet.getUserContext();
        checkUserAuthorization(userContext);

        org.gatein.pc.api.PortletContext portalPC = WSRPUtils.convertToPortalPortletContext(portletContext);
        try {
            RegistrationLocal.setRegistration(registration);
            org.gatein.pc.api.PortletContext response = producer.getPortletInvoker()
                    .createClone(PortletStateType.OPAQUE, portalPC);
            return WSRPUtils.convertToWSRPPortletContext(response);
        } catch (NoSuchPortletException e) {
            throw WSRP2ExceptionFactory.throwWSException(InvalidHandle.class,
                    "Failed to create clone for portlet '" + portletContext.getPortletHandle(), e);
        } catch (InvalidPortletIdException e) {
            throw WSRP2ExceptionFactory.throwWSException(InconsistentParameters.class,
                    "Failed to create clone for portlet '" + portletContext.getPortletHandle(), e);
        } catch (PortletInvokerException e) {
            throw WSRP2ExceptionFactory.throwWSException(OperationFailed.class,
                    "Failed to create clone for portlet '" + portletContext.getPortletHandle(), e);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    public DestroyPortletsResponse destroyPortlets(DestroyPortlets destroyPortlets)
            throws InconsistentParameters, InvalidRegistration, MissingParameters, ModifyRegistrationRequired,
            OperationFailed, OperationNotSupported, ResourceSuspended {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(destroyPortlets, "DestroyPortlets");

        List<String> handles = destroyPortlets.getPortletHandles();
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(handles, "portlet handles to be destroyed",
                "DestroyPortlets");
        handles = WSRPUtils.replaceByEmptyListIfNeeded(handles);

        Registration registration = producer
                .getRegistrationOrFailIfInvalid(destroyPortlets.getRegistrationContext());

        List<org.gatein.pc.api.PortletContext> portletContexts = new ArrayList<org.gatein.pc.api.PortletContext>(
                handles.size());
        for (String handle : handles) {
            portletContexts.add(org.gatein.pc.api.PortletContext.createPortletContext(handle));
        }

        try {
            RegistrationLocal.setRegistration(registration);
            List<DestroyCloneFailure> failuresList = producer.getPortletInvoker().destroyClones(portletContexts);
            int failuresNumber = failuresList.size();
            List<FailedPortlets> failedPortlets;
            if (failuresNumber > 0) {
                // for each reason of failure, record the associated portlet handles, expecting one portlet handle per message
                Multimap<String, String> reasonToHandles = HashMultimap.create(failuresNumber, 1);
                for (DestroyCloneFailure failure : failuresList) {
                    reasonToHandles.put(failure.getMessage(), failure.getPortletId());
                }

                // create a FailedPortlets object for each reason
                failedPortlets = new ArrayList<FailedPortlets>(reasonToHandles.size());
                for (String reason : reasonToHandles.keys()) {
                    failedPortlets.add(WSRPTypeFactory.createFailedPortlets(reasonToHandles.get(reason),
                            ErrorCodes.Codes.OPERATIONFAILED, reason));
                }
            } else {
                failedPortlets = null;
            }

            return WSRPTypeFactory.createDestroyPortletsResponse(failedPortlets);
        } catch (PortletInvokerException e) {
            throw WSRP2ExceptionFactory.throwWSException(OperationFailed.class, "Failed to destroy clones", e);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    public GetPortletsLifetimeResponse getPortletsLifetime(GetPortletsLifetime getPortletsLifetime)
            throws AccessDenied, InconsistentParameters, InvalidHandle, InvalidRegistration,
            ModifyRegistrationRequired, OperationFailed, OperationNotSupported, ResourceSuspended {
        throw WSRP2ExceptionFactory.throwWSException(OperationNotSupported.class,
                "Lifetime operations are not currently supported.", null);
    }

    public SetPortletsLifetimeResponse setPortletsLifetime(SetPortletsLifetime setPortletsLifetime)
            throws AccessDenied, InconsistentParameters, InvalidHandle, InvalidRegistration,
            ModifyRegistrationRequired, OperationFailed, OperationNotSupported, ResourceSuspended {
        throw WSRP2ExceptionFactory.throwWSException(OperationNotSupported.class,
                "Lifetime operations are not currently supported.", null);
    }

    public CopyPortletsResponse copyPortlets(CopyPortlets copyPortlets) throws AccessDenied, InconsistentParameters,
            InvalidHandle, InvalidRegistration, InvalidUserCategory, MissingParameters, ModifyRegistrationRequired,
            OperationFailed, OperationNotSupported, ResourceSuspended {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(copyPortlets, "copyPortlets");

        List<PortletContext> portletContexts = copyPortlets.getFromPortletContexts();
        if (!ParameterValidation.existsAndIsNotEmpty(portletContexts)
                || WSRPUtils.isSingletonListWithNullOrEmptyElement(portletContexts)) {
            throw WSRP2ExceptionFactory.createWSException(MissingParameters.class,
                    "Missing required portletContext in CopyPortlets.", null);
        }

        Registration fromRegistration = producer
                .getRegistrationOrFailIfInvalid(copyPortlets.getFromRegistrationContext());

        RegistrationContext toRegistationContext = copyPortlets.getToRegistrationContext();

        //if toRegistrationContext is null, then we use the fromRegistrationContext (from spec).
        //NOTE: this means we can't move between a PortletContext on a registered consumer to a non-registered consumer
        // between two non-registered consumers will still be ok.
        if (toRegistationContext == null) {
            toRegistationContext = copyPortlets.getFromRegistrationContext();
        }

        Registration toRegistration = producer.getRegistrationOrFailIfInvalid(toRegistationContext);

        UserContext fromUserContext = copyPortlets.getFromUserContext();
        checkUserAuthorization(fromUserContext);
        UserContext toUserContext = copyPortlets.getToUserContext();
        checkUserAuthorization(toUserContext);

        try {
            RegistrationLocal.setRegistration(fromRegistration);

            Map<String, FailedPortlets> failedPortletsMap = new HashMap<String, FailedPortlets>(
                    portletContexts.size());

            List<CopiedPortlet> copiedPortlets = new ArrayList<CopiedPortlet>(portletContexts.size());

            for (PortletContext portletContext : portletContexts) {
                try {
                    org.gatein.pc.api.PortletContext portalPC = WSRPUtils
                            .convertToPortalPortletContext(portletContext);

                    //NOTE: There are two ways we can do a copy. We can export using one registration and import using another. This seems the most straightforward way to do this, just seems a little overkill.
                    // OR we can copy the portlet, then use the RegistrationManager and RegistrationPolicy to delete the PC from one registration and add it to another. But we don't actually 
                    // create the copy under the toRegistration and we would need to add extra checks here to make sure the toRegistration has the proper permissions.
                    // Not sure why there is even a copy portlet operation since it can be replicated by an export and then an import operation.

                    org.gatein.pc.api.PortletContext exportedPortletContext = producer.getPortletInvoker()
                            .exportPortlet(PortletStateType.OPAQUE, portalPC);
                    //Change the registration to the new registration and try and do an import. This should force the new import to be under the new registration context
                    RegistrationLocal.setRegistration(toRegistration);
                    org.gatein.pc.api.PortletContext copiedPortletContext = producer.getPortletInvoker()
                            .importPortlet(PortletStateType.OPAQUE, exportedPortletContext);

                    PortletContext wsrpClonedPC = WSRPUtils.convertToWSRPPortletContext(copiedPortletContext);

                    CopiedPortlet copiedPortlet = WSRPTypeFactory.createCopiedPortlet(wsrpClonedPC,
                            portletContext.getPortletHandle());
                    copiedPortlets.add(copiedPortlet);
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("Error occured while trying to export a portlet.", e);
                    }

                    ErrorCodes.Codes errorCode;
                    String reason;
                    final String message = e.getLocalizedMessage();
                    if (e instanceof NoSuchPortletException || e instanceof InvalidHandle
                            || (e instanceof IllegalArgumentException && message != null && message
                                    .contains(org.gatein.pc.api.PortletContext.INVALID_PORTLET_CONTEXT))) {
                        errorCode = ErrorCodes.Codes.INVALIDHANDLE;
                        reason = "The specified portlet handle is invalid";
                    } else // default error message.
                    {
                        errorCode = ErrorCodes.Codes.OPERATIONFAILED;
                        reason = "Error preparing portlet for export";
                    }

                    if (!failedPortletsMap.containsKey(errorCode.name())) {
                        List<String> portletHandles = new ArrayList<String>();
                        portletHandles.add(portletContext.getPortletHandle());

                        FailedPortlets failedPortlets = WSRPTypeFactory.createFailedPortlets(portletHandles,
                                errorCode, reason);
                        failedPortletsMap.put(errorCode.name(), failedPortlets);
                    } else {
                        FailedPortlets failedPortlets = failedPortletsMap.get(errorCode.name());
                        failedPortlets.getPortletHandles().add(portletContext.getPortletHandle());
                    }
                }
            }

            List<FailedPortlets> failedPortlets = new ArrayList<FailedPortlets>(failedPortletsMap.values());
            //TODO: handle resources properly
            ResourceList resourceList = null;
            return WSRPTypeFactory.createCopyPortletsResponse(copiedPortlets, failedPortlets, resourceList);
        } catch (Exception e) {
            throw WSRP2ExceptionFactory.throwWSException(OperationFailed.class,
                    "Operation Failed while trying to CopyPortlets.", e);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    public PortletContext setPortletProperties(SetPortletProperties setPortletProperties) throws AccessDenied,
            InconsistentParameters, InvalidHandle, InvalidRegistration, InvalidUserCategory, MissingParameters,
            ModifyRegistrationRequired, OperationFailed, OperationNotSupported, ResourceSuspended {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(setPortletProperties, "SetPortletProperties");

        PortletContext portletContext = setPortletProperties.getPortletContext();
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(portletContext, "PortletContext",
                "SetPortletProperties");

        PropertyList propertyList = setPortletProperties.getPropertyList();
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(propertyList, "PropertyList",
                "SetPortletProperties");

        Registration registration = producer
                .getRegistrationOrFailIfInvalid(setPortletProperties.getRegistrationContext());

        checkUserAuthorization(setPortletProperties.getUserContext());

        List<Property> properties = propertyList.getProperties();
        properties = WSRPUtils.replaceByEmptyListIfNeeded(properties);
        List<ResetProperty> resetProperties = propertyList.getResetProperties();
        resetProperties = WSRPUtils.replaceByEmptyListIfNeeded(resetProperties);
        int changesCount = 0;
        if (ParameterValidation.existsAndIsNotEmpty(properties)) {
            changesCount += properties.size();

            // check that we don't set and reset the same property
            if (ParameterValidation.existsAndIsNotEmpty(resetProperties)) {
                List<QName> names = new ArrayList<QName>(
                        WSRPUtils.transform(properties, new Function<Property, QName>() {
                            public QName apply(Property from) {
                                return from.getName();
                            }
                        }));
                names.retainAll(WSRPUtils.transform(resetProperties, new Function<ResetProperty, QName>() {
                    public QName apply(ResetProperty from) {
                        return from.getName();
                    }
                }));

                if (!names.isEmpty()) {
                    WSRP2ExceptionFactory.throwWSException(InconsistentParameters.class,
                            "Attempted to set and reset at the same time the following properties: " + names, null);
                }
            }
        }
        if (ParameterValidation.existsAndIsNotEmpty(resetProperties)) {
            changesCount += resetProperties.size();
        }

        if (changesCount > 0) {
            List<PropertyChange> changes = new ArrayList<PropertyChange>(changesCount);

            if (properties != null) {
                for (Property property : properties) {
                    String value = property.getStringValue();

                    // todo: deal with XML values...
                    // List<Object> values = property.getAny();
                    // todo: deal with language?
                    // String lang = property.getLang(); 

                    changes.add(PropertyChange.newUpdate(property.getName().toString(), value));
                }
            }

            if (resetProperties != null) {
                for (ResetProperty resetProperty : resetProperties) {
                    changes.add(PropertyChange.newReset(resetProperty.getName().toString()));
                }
            }

            try {
                RegistrationLocal.setRegistration(registration);
                org.gatein.pc.api.PortletContext resultContext = producer.getPortletInvoker().setProperties(
                        WSRPUtils.convertToPortalPortletContext(portletContext),
                        changes.toArray(new PropertyChange[changes.size()]));
                return WSRPUtils.convertToWSRPPortletContext(resultContext);
            } catch (NoSuchPortletException e) {
                throw WSRP2ExceptionFactory.throwWSException(InvalidHandle.class,
                        "Failed to set properties for portlet '" + portletContext.getPortletHandle() + "'", e);
            } catch (InvalidPortletIdException e) {
                throw WSRP2ExceptionFactory.throwWSException(InconsistentParameters.class,
                        "Failed to set properties for portlet '" + portletContext.getPortletHandle() + "'", e);
            } catch (PortletInvokerException e) {
                throw WSRP2ExceptionFactory.throwWSException(OperationFailed.class,
                        "Failed to set properties for portlet '" + portletContext.getPortletHandle() + "'", e);
            } finally {
                RegistrationLocal.setRegistration(null);
            }
        }

        return portletContext;
    }

    public PropertyList getPortletProperties(GetPortletProperties getPortletProperties) throws AccessDenied,
            InconsistentParameters, InvalidHandle, InvalidRegistration, InvalidUserCategory, MissingParameters,
            ModifyRegistrationRequired, OperationFailed, OperationNotSupported, ResourceSuspended {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(getPortletProperties, GET_PORTLET_PROPERTIES);

        PortletContext portletContext = getPortletProperties.getPortletContext();
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(portletContext, PORTLET_CONTEXT,
                GET_PORTLET_PROPERTIES);

        Registration registration = producer
                .getRegistrationOrFailIfInvalid(getPortletProperties.getRegistrationContext());

        UserContext userContext = getPortletProperties.getUserContext();
        checkUserAuthorization(userContext);

        List<String> names = getPortletProperties.getNames();
        names = WSRPUtils.replaceByEmptyListIfNeeded(names);

        Set<String> keys = new HashSet<String>(names);

        try {
            PropertyMap properties;
            org.gatein.pc.api.PortletContext jbpContext = WSRPUtils.convertToPortalPortletContext(portletContext);

            RegistrationLocal.setRegistration(registration);
            if (!keys.isEmpty()) {
                properties = producer.getPortletInvoker().getProperties(jbpContext, keys);
            } else {
                properties = producer.getPortletInvoker().getProperties(jbpContext);
            }

            //todo: we need to check that the user can actually modify the properties
            Portlet portlet = getPortletFrom(portletContext, registration);
            PortletInfo info = portlet.getInfo();

            PropertyList result = WSRPTypeFactory.createPropertyList();
            int propertyNb = properties.size();

            if (propertyNb > 0) {
                PreferenceInfo prefInfo;
                String key;
                List<String> values;
                LocalizedString displayName;

                for (Map.Entry<String, List<String>> entry : properties.entrySet()) {
                    key = entry.getKey();
                    values = entry.getValue();
                    prefInfo = info.getPreferences().getPreference(key);
                    String lang = WSRPConstants.DEFAULT_LOCALE;
                    if (prefInfo != null) {
                        displayName = prefInfo.getDisplayName();
                        if (displayName != null) {
                            lang = WSRPUtils.toString(displayName.getDefaultLocale());
                        }
                    }

                    // todo: support multi-valued properties
                    if (values.size() != 1) {
                        throw new UnsupportedOperationException(
                                "Currently doesn't support multi-valued properties!");
                    }
                    result.getProperties().add(WSRPTypeFactory.createProperty(key, lang, values.get(0))); //todo: check what we should use key
                }
            }

            return result;
        } catch (PortletInvokerException e) {
            throw WSRP2ExceptionFactory.throwWSException(InvalidHandle.class,
                    "Could not retrieve properties for portlet '" + portletContext + "'", e);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    public ExportPortletsResponse exportPortlets(ExportPortlets exportPortlets)
            throws AccessDenied, ExportByValueNotSupported, InconsistentParameters, InvalidHandle,
            InvalidRegistration, InvalidUserCategory, MissingParameters, ModifyRegistrationRequired,
            OperationFailed, OperationNotSupported, ResourceSuspended {
        WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(exportPortlets, "ExportPortlets",
                "ExportPortlets");

        List<PortletContext> portletContexts = exportPortlets.getPortletContext();
        if (!ParameterValidation.existsAndIsNotEmpty(portletContexts)
                || WSRPUtils.isSingletonListWithNullOrEmptyElement(portletContexts)) {
            throw WSRP2ExceptionFactory.createWSException(MissingParameters.class,
                    "Missing required list of portlets to export in ExportPortlets.", null);
        }

        Registration registration = producer
                .getRegistrationOrFailIfInvalid(exportPortlets.getRegistrationContext());

        UserContext userContext = exportPortlets.getUserContext();
        checkUserAuthorization(userContext);

        boolean exportByValueRequired;
        if (exportPortlets.isExportByValueRequired() != null) {
            exportByValueRequired = exportPortlets.isExportByValueRequired();
        } else {
            exportByValueRequired = false;
        }

        //check that the export manager can handle export by value
        final ExportManager exportManager = producer.getExportManager();
        if (exportByValueRequired && !exportManager.supportsExportByValue()) {
            //TODO: instead of passing a string here, we should pass a resource so that its localized
            WSRP2ExceptionFactory.throwWSException(ExportByValueNotSupported.class,
                    "The consumer is requesting portlets to be exported by value, but this consumer only supports export by reference.",
                    null);
        }

        List<ExportedPortlet> exportedPortlets = new ArrayList<ExportedPortlet>(portletContexts.size());
        Map<String, FailedPortlets> failedPortletsMap = new HashMap<String, FailedPortlets>(portletContexts.size());

        try {
            RegistrationLocal.setRegistration(registration);

            ExportContext exportContext;
            final Lifetime askedLifetime = exportPortlets.getLifetime();
            if (askedLifetime != null) {
                final XMLGregorianCalendar askedLifetimeCurrentTime = askedLifetime.getCurrentTime();
                long currentTime = toLongDate(askedLifetimeCurrentTime);
                long terminationTime = toLongDate(askedLifetime.getTerminationTime());
                long refreshDuration = askedLifetime.getRefreshDuration()
                        .getTimeInMillis(askedLifetimeCurrentTime.toGregorianCalendar());
                exportContext = exportManager.createExportContext(exportByValueRequired, currentTime,
                        terminationTime, refreshDuration);
            } else {
                exportContext = exportManager.createExportContext(exportByValueRequired, -1, -1, -1);
            }

            for (PortletContext portletContext : portletContexts) {
                try {
                    WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(portletContext, "Portlet context");
                    String portletHandle = portletContext.getPortletHandle();
                    WSRP2ExceptionFactory.throwMissingParametersIfValueIsMissing(portletHandle, "Portlet handle",
                            "PortletContext");

                    org.gatein.pc.api.PortletContext portalPC = WSRPUtils
                            .convertToPortalPortletContext(portletContext);

                    org.gatein.pc.api.PortletContext exportedPortalPC = producer.getPortletInvoker()
                            .exportPortlet(PortletStateType.OPAQUE, portalPC);
                    if (exportedPortalPC == null) {
                        WSRP2ExceptionFactory.throwWSException(InvalidHandle.class,
                                "Could not find a portlet with handle " + portletHandle + " in the producer", null);
                    }

                    byte[] portletState = WSRPUtils.getStateOrNullFor(exportedPortalPC);

                    //get the exportPortletData
                    ExportPortletData exportPortletData = exportManager.createExportPortletData(exportContext,
                            portletHandle, portletState);

                    //Create the exportedPortlet
                    byte[] exportPortletBytes = exportManager.encodeExportPortletData(exportContext,
                            exportPortletData);
                    ExportedPortlet exportedPortlet = WSRPTypeFactory.createExportedPortlet(portletHandle,
                            exportPortletBytes);
                    exportedPortlets.add(exportedPortlet);
                }

                catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("Error occured while trying to export a portlet.", e);
                    }

                    ErrorCodes.Codes errorCode;
                    String reason;
                    if (e instanceof NoSuchPortletException || e instanceof InvalidHandle) {
                        errorCode = ErrorCodes.Codes.INVALIDHANDLE;
                        reason = "The specified portlet handle is invalid";
                    } else // default error message.
                    {
                        errorCode = ErrorCodes.Codes.OPERATIONFAILED;
                        reason = "Error preparing portlet for export";
                    }

                    final String errorCodeName = errorCode.name();
                    if (!failedPortletsMap.containsKey(errorCodeName)) {
                        List<String> portletHandles = new ArrayList<String>();
                        portletHandles.add(portletContext.getPortletHandle());

                        FailedPortlets failedPortlets = WSRPTypeFactory.createFailedPortlets(portletHandles,
                                errorCode, reason);
                        failedPortletsMap.put(errorCodeName, failedPortlets);
                    } else {
                        FailedPortlets failedPortlets = failedPortletsMap.get(errorCodeName);
                        failedPortlets.getPortletHandles().add(portletContext.getPortletHandle());
                    }
                }
            }

            //TODO: handle resourceLists better (should be using for things like errors)
            ResourceList resourceList = null;

            byte[] exportContextBytes = exportManager.encodeExportContextData(exportContext);

            Lifetime lifetime = null;

            if (exportContext.getCurrentTime() > 0) {
                lifetime = new Lifetime();
                lifetime.setCurrentTime(toXMLGregorianCalendar(exportContext.getCurrentTime()));
                lifetime.setTerminationTime(toXMLGregorianCalendar(exportContext.getTermintationTime()));
                lifetime.setRefreshDuration(toDuration(exportContext.getRefreshDuration()));
            }

            return WSRPTypeFactory.createExportPortletsResponse(exportContextBytes, exportedPortlets,
                    new ArrayList<FailedPortlets>(failedPortletsMap.values()), lifetime, resourceList);
        } catch (Exception e) {
            throw WSRP2ExceptionFactory.throwWSException(OperationFailed.class,
                    "Operation Failed while trying to ExportPortlets.", e);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    public ImportPortletsResponse importPortlets(ImportPortlets importPortlets)
            throws OperationFailed, InvalidRegistration, MissingParameters, ModifyRegistrationRequired {
        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(importPortlets, "ImportPortlets");

        List<ImportPortlet> importPortletList = importPortlets.getImportPortlet();
        if (!ParameterValidation.existsAndIsNotEmpty(importPortletList)
                || WSRPUtils.isSingletonListWithNullOrEmptyElement(importPortletList)) {
            throw WSRP2ExceptionFactory.createWSException(MissingParameters.class,
                    "Missing required list of portlets to import in ImportPortlets.", null);
        }

        Registration registration = producer
                .getRegistrationOrFailIfInvalid(importPortlets.getRegistrationContext());

        // check if we have a valid userContext or not
        UserContext userContext = importPortlets.getUserContext();
        checkUserAuthorization(userContext);

        try {
            RegistrationLocal.setRegistration(registration);

            byte[] importContext = importPortlets.getImportContext();

            Lifetime lifeTime = importPortlets.getLifetime();

            List<ImportedPortlet> importedPortlets = new ArrayList<ImportedPortlet>();
            Map<String, ImportPortletsFailed> failedPortletsMap = new HashMap<String, ImportPortletsFailed>();

            ExportContext exportContext;
            final ExportManager exportManager = producer.getExportManager();
            try {
                exportContext = exportManager.createExportContext(importContext);
            } catch (Exception e) {
                throw WSRPExceptionFactory.createWSException(OperationFailed.class, "Invalid ImportContext.", e);
            }

            for (ImportPortlet importPortlet : importPortletList) {
                try {
                    byte[] portletData = importPortlet.getExportData();

                    ExportPortletData exportPortletData;
                    if (lifeTime != null) {
                        long currentTime = toLongDate(lifeTime.getCurrentTime());
                        long terminationTime = toLongDate(lifeTime.getTerminationTime());
                        long refreshDuration = lifeTime.getRefreshDuration()
                                .getTimeInMillis(lifeTime.getCurrentTime().toGregorianCalendar());
                        exportPortletData = exportManager.createExportPortletData(exportContext, currentTime,
                                terminationTime, refreshDuration, portletData);
                    } else {
                        exportPortletData = exportManager.createExportPortletData(exportContext, -1, -1, -1,
                                portletData);
                    }

                    String portletHandle = exportPortletData.getPortletHandle();
                    byte[] portletState = exportPortletData.getPortletState();

                    PortletContext pc = WSRPTypeFactory.createPortletContext(portletHandle, portletState);
                    org.gatein.pc.api.PortletContext pcPortletContext = WSRPUtils.convertToPortalPortletContext(pc);

                    org.gatein.pc.api.PortletContext cpc = producer.getPortletInvoker()
                            .importPortlet(PortletStateType.OPAQUE, pcPortletContext);
                    PortletContext wpc = WSRPUtils.convertToWSRPPortletContext(cpc);

                    ImportedPortlet importedPortlet = WSRPTypeFactory
                            .createImportedPortlet(importPortlet.getImportID(), wpc);

                    importedPortlets.add(importedPortlet);
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("Error occured while trying to import a portlet.", e);
                    }

                    ErrorCodes.Codes errorCode;
                    String reason;
                    if (e instanceof NoSuchPortletException || e instanceof InvalidHandle) {
                        errorCode = ErrorCodes.Codes.INVALIDHANDLE;
                        reason = "The specified portlet handle is invalid";
                    } else if (e instanceof OperationFailed) {
                        errorCode = ErrorCodes.Codes.OPERATIONFAILED;
                        reason = e.getMessage();
                    } else if (e instanceof PortletInvokerException || e instanceof UnsupportedOperationException
                            || e instanceof IllegalArgumentException) {
                        errorCode = ErrorCodes.Codes.OPERATIONFAILED;
                        reason = "Error trying to create imported portlet.";
                    } else // default error message.
                    {
                        errorCode = ErrorCodes.Codes.OPERATIONFAILED;
                        reason = "Error importing portlet.";
                    }

                    if (!failedPortletsMap.containsKey(errorCode.name())) {
                        List<String> portleIDs = new ArrayList<String>();
                        portleIDs.add(importPortlet.getImportID());

                        ImportPortletsFailed failedPortlets = WSRPTypeFactory.createImportPortletsFailed(portleIDs,
                                errorCode, reason);
                        failedPortletsMap.put(errorCode.name(), failedPortlets);
                    } else {
                        ImportPortletsFailed failedPortlets = failedPortletsMap.get(errorCode.name());
                        failedPortlets.getImportID().add(importPortlet.getImportID());
                    }
                }
            }

            ResourceList resourceList = null; //TODO: figure out what exactly should be stored in the resource list here

            return WSRPTypeFactory.createImportPortletsResponse(importedPortlets,
                    new ArrayList<ImportPortletsFailed>(failedPortletsMap.values()), resourceList);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    public List<Extension> releaseExport(ReleaseExport releaseExport) {
        try {
            if (releaseExport != null && releaseExport.getExportContext() != null) {
                producer.getExportManager().releaseExport(releaseExport.getExportContext());
            }
        } catch (Exception e) {
            if (log.isWarnEnabled()) {
                log.warn("Error occured while trying to perform a ReleaseExport", e);
            }
        }

        //this method shouldn't return anything
        return WSRPTypeFactory.createReturnAny().getExtensions();
    }

    public Lifetime setExportLifetime(SetExportLifetime setExportLifetime)
            throws OperationFailed, InvalidRegistration, OperationNotSupported, ModifyRegistrationRequired {
        //this method is only valid if the producer can handle exporting by reference.
        if (producer.getExportManager().getPersistenceManager() == null) {
            WSRP2ExceptionFactory.throwWSException(OperationNotSupported.class,
                    "This producer does not support export by reference.", null);
        }

        WSRP2ExceptionFactory.throwOperationFailedIfValueIsMissing(setExportLifetime, "setExportLifetimePortlets");

        byte[] exportContextBytes = setExportLifetime.getExportContext();
        //NOTE: we can't throw a MissingParameterException since its not allowed as part of the spec
        if (exportContextBytes == null) {
            WSRPExceptionFactory.throwWSException(OperationFailed.class,
                    "Cannot call setExportLifetime with an empty ExportContext.", null);
        }

        Registration registration = producer
                .getRegistrationOrFailIfInvalid(setExportLifetime.getRegistrationContext());

        // check if we have a valid userContext or not
        UserContext userContext = setExportLifetime.getUserContext();
        checkUserAuthorization(userContext);

        try {
            RegistrationLocal.setRegistration(registration);

            ExportContext exportContext;
            long currentTime = toLongDate(setExportLifetime.getLifetime().getCurrentTime());
            long terminationTime = toLongDate(setExportLifetime.getLifetime().getTerminationTime());
            long refreshDuration = setExportLifetime.getLifetime().getRefreshDuration()
                    .getTimeInMillis(setExportLifetime.getLifetime().getCurrentTime().toGregorianCalendar());
            exportContext = producer.getExportManager().setExportLifetime(exportContextBytes, currentTime,
                    terminationTime, refreshDuration);

            return getLifetime(exportContext);
        } catch (Exception e) {
            throw WSRPExceptionFactory.createWSException(OperationFailed.class,
                    "Operation Failed while trying to setExportLifetime.", e);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    private void checkUserAuthorization(UserContext userContext) {
        //todo: implement
        if (userContext != null) {

        }
    }

    private Portlet getPortletFrom(PortletContext portletContext, Registration registration) throws InvalidHandle {
        Portlet portlet;
        try {
            RegistrationLocal.setRegistration(registration);
            portlet = producer.getPortletInvoker()
                    .getPortlet(WSRPUtils.convertToPortalPortletContext(portletContext));
            return portlet;
        } catch (PortletInvokerException e) {
            throw WSRP2ExceptionFactory.throwWSException(InvalidHandle.class,
                    "Could not retrieve portlet '" + portletContext.getPortletHandle() + "'", e);
        } finally {
            RegistrationLocal.setRegistration(null);
        }
    }

    //TODO: move these classes to the common module and write up proper lifetime utilities 
    private XMLGregorianCalendar toXMLGregorianCalendar(long time) throws DatatypeConfigurationException {
        Date date = new Date(time);
        GregorianCalendar gregorianCalendar = new GregorianCalendar();
        gregorianCalendar.setTime(date);
        return DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar);
    }

    private long toLongDate(XMLGregorianCalendar calendar) {
        return calendar.toGregorianCalendar().getTime().getTime();
    }

    private Duration toDuration(long duration) throws DatatypeConfigurationException {
        return DatatypeFactory.newInstance().newDuration(duration);
    }

    private Lifetime getLifetime(ExportContext exportContext) throws DatatypeConfigurationException {
        if (exportContext.getCurrentTime() >= 0) {
            Lifetime lifetime = new Lifetime();

            XMLGregorianCalendar currentTime = toXMLGregorianCalendar(exportContext.getCurrentTime());
            XMLGregorianCalendar terminationTime = toXMLGregorianCalendar(exportContext.getTermintationTime());

            Duration duration = toDuration(exportContext.getRefreshDuration());

            lifetime.setCurrentTime(currentTime);
            lifetime.setTerminationTime(terminationTime);
            lifetime.setRefreshDuration(duration);

            return lifetime;
        } else {
            return null;
        }
    }
}