org.apereo.portal.io.xml.portlet.ExternalPortletDefinitionUnmarshaller.java Source code

Java tutorial

Introduction

Here is the source code for org.apereo.portal.io.xml.portlet.ExternalPortletDefinitionUnmarshaller.java

Source

/**
 * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright ownership. Apereo
 * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the License at the
 * following location:
 *
 * <p>http://www.apache.org/licenses/LICENSE-2.0
 *
 * <p>Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apereo.portal.io.xml.portlet;

import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import org.apereo.portal.IUserIdentityStore;
import org.apereo.portal.portlet.dao.IPortletDefinitionDao;
import org.apereo.portal.portlet.dao.jpa.PortletDefinitionImpl;
import org.apereo.portal.portlet.dao.jpa.PortletDefinitionParameterImpl;
import org.apereo.portal.portlet.dao.jpa.PortletPreferenceImpl;
import org.apereo.portal.portlet.om.IPortletDefinition;
import org.apereo.portal.portlet.om.IPortletDefinitionParameter;
import org.apereo.portal.portlet.om.IPortletDescriptorKey;
import org.apereo.portal.portlet.om.IPortletLifecycleEntry;
import org.apereo.portal.portlet.om.IPortletPreference;
import org.apereo.portal.portlet.om.IPortletType;
import org.apereo.portal.portlet.om.PortletLifecycleState;
import org.apereo.portal.portlet.registry.IPortletCategoryRegistry;
import org.apereo.portal.portlet.registry.IPortletTypeRegistry;
import org.apereo.portal.security.IPerson;
import org.apereo.portal.security.PersonFactory;
import org.apereo.portal.xml.PortletDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Responsible for taking an {@link ExternalPortletDefinition} and producing an {@link
 * IPortletDefinition}.
 */
@Component
/* package-private */ class ExternalPortletDefinitionUnmarshaller {

    /**
     * Represents a special datetime value that helps us solve a problem we have with supporting
     * older portlet-definition entities in MAINTENANCE mode. We used to handle MAINTENANCE with a
     * parameter; now it is a first class lifecycle state in the database and in XML. Lifecycle
     * states require a data, but the older, parameter-based MAINTENANCE model doesn't include one.
     * We will use the current instant in these cases, but we need a way for the XML to signal that
     * we should do so. According to the XSD, the datetime value cannot be empty in any way. This
     * datetime is the last second before the present millennium -- more than 10 years before
     * MAINTENANCE mode was implemented. When we see this datetime, we will substitute the present
     * datetime.
     */
    /* package-private */ static final String USE_CURRENT_DATETIME_SIGNAL_DATE = "1999-12-31T23:59:59.000Z";

    @Value("${org.apereo.portal.io.errorOnChannel:false}")
    private boolean errorOnChannel;

    @Autowired
    private IPortletTypeRegistry portletTypeRegistry;

    @Autowired
    private IPortletCategoryRegistry portletCategoryRegistry;

    @Autowired
    private IPortletDefinitionDao portletDefinitionDao;

    @Autowired
    private IUserIdentityStore userIdentityStore;

    private final Date useCurrentDatetimeSignal;

    private final IPerson systemUser = PersonFactory.createSystemPerson();

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public ExternalPortletDefinitionUnmarshaller() {
        final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
        try {
            useCurrentDatetimeSignal = dateFormat.parse(USE_CURRENT_DATETIME_SIGNAL_DATE);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    /** For unit tests. */
    /* package-private */ void setUserIdentityStore(IUserIdentityStore userIdentityStore) {
        this.userIdentityStore = userIdentityStore;
    }

    /* package-private */ IPortletDefinition unmarshall(ExternalPortletDefinition epd) {

        final PortletDescriptor portletDescriptor = epd.getPortletDescriptor();
        final Boolean isFramework = portletDescriptor.isIsFramework();

        if (isFramework != null && isFramework
                && "UPGRADED_CHANNEL_IS_NOT_A_PORTLET".equals(portletDescriptor.getPortletName())) {
            if (errorOnChannel) {
                throw new IllegalArgumentException(epd.getFname()
                        + " is not a portlet. It was likely an IChannel from a previous version of uPortal and cannot be imported.");
            }

            logger.warn(epd.getFname()
                    + " is not a portlet. It was likely an IChannel from a previous version of uPortal and will not be imported.");
            return null;
        }

        // get the portlet type
        final IPortletType portletType = portletTypeRegistry.getPortletType(epd.getType());
        if (portletType == null) {
            throw new IllegalArgumentException("No portlet type registered for: " + epd.getType());
        }

        final String fname = epd.getFname();
        IPortletDefinition rslt = portletDefinitionDao.getPortletDefinitionByFname(fname);
        if (rslt == null) {
            rslt = new PortletDefinitionImpl(portletType, fname, epd.getName(), epd.getTitle(),
                    portletDescriptor.getWebAppName(), portletDescriptor.getPortletName(),
                    isFramework != null ? isFramework : false);
        } else {
            final IPortletDescriptorKey portletDescriptorKey = rslt.getPortletDescriptorKey();
            portletDescriptorKey.setPortletName(portletDescriptor.getPortletName());
            if (isFramework != null && isFramework) {
                portletDescriptorKey.setFrameworkPortlet(true);
                portletDescriptorKey.setWebAppName(null);
            } else {
                portletDescriptorKey.setFrameworkPortlet(false);
                portletDescriptorKey.setWebAppName(portletDescriptor.getWebAppName());
            }
            rslt.setName(epd.getName());
            rslt.setTitle(epd.getTitle());
            rslt.setType(portletType);
        }

        rslt.setDescription(epd.getDesc());
        final BigInteger timeout = epd.getTimeout();
        if (timeout != null) {
            rslt.setTimeout(timeout.intValue());
        }
        final BigInteger actionTimeout = epd.getActionTimeout();
        if (actionTimeout != null) {
            rslt.setActionTimeout(actionTimeout.intValue());
        }
        final BigInteger eventTimeout = epd.getEventTimeout();
        if (eventTimeout != null) {
            rslt.setEventTimeout(eventTimeout.intValue());
        }
        final BigInteger renderTimeout = epd.getRenderTimeout();
        if (renderTimeout != null) {
            rslt.setRenderTimeout(renderTimeout.intValue());
        }
        final BigInteger resourceTimeout = epd.getResourceTimeout();
        if (resourceTimeout != null) {
            rslt.setResourceTimeout(resourceTimeout.intValue());
        }

        unmarshallLifecycle(epd.getLifecycle(), rslt);

        final Set<IPortletDefinitionParameter> parameters = new LinkedHashSet<>();
        for (ExternalPortletParameter param : epd.getParameters()) {
            parameters.add(new PortletDefinitionParameterImpl(param.getName(), param.getValue()));
        }
        rslt.setParameters(parameters);

        final ArrayList<IPortletPreference> preferenceList = new ArrayList<>();
        for (ExternalPortletPreference pref : epd.getPortletPreferences()) {
            final List<String> valueList = pref.getValues();
            final String[] values = valueList.toArray(new String[valueList.size()]);

            final Boolean readOnly = pref.isReadOnly();
            preferenceList
                    .add(new PortletPreferenceImpl(pref.getName(), readOnly != null ? readOnly : false, values));
        }
        rslt.setPortletPreferences(preferenceList);

        return rslt;
    }

    /* package-private */ void unmarshallLifecycle(final Lifecycle lifecycle,
            final IPortletDefinition portletDefinition) {

        /*
         * If this is an existing portletDefinition, it may (probably does) already contain
         * lifecycle entries.  We need to remove those, because the lifecycle of a portlet after
         * import should reflect what the document says exactly.
         */
        portletDefinition.clearLifecycle();

        if (lifecycle == null) {
            /*
             * For backwards-compatibility, a complete absence of
             * lifecycle information means the portlet is published.
             */
            portletDefinition.updateLifecycleState(PortletLifecycleState.PUBLISHED, systemUser);
        } else if (lifecycle.getEntries().isEmpty()) {
            /*
             * According to the comments for 4.3, we're supposed
             * to leave the portlet in CREATED state.
             */
            portletDefinition.updateLifecycleState(PortletLifecycleState.CREATED, systemUser);
        } else {
            /*
             * Use a TreeMap because we need to be certain the the entries
             * get applied to the new portlet definition in a sane order...
             */
            Map<IPortletLifecycleEntry, IPerson> convertedEntries = new TreeMap<>();
            /*
             * Convert each LifecycleEntry (JAXB) to an IPortletLifecycleEntry (internal)
             */
            for (LifecycleEntry entry : lifecycle.getEntries()) {
                final IPerson user = StringUtils.isNotBlank(entry.getUser())
                        ? userIdentityStore.getPerson(entry.getUser(), true)
                        : systemUser; // default
                // We will support case insensitivity of entry/@name in the XML
                final PortletLifecycleState state = PortletLifecycleState.valueOf(entry.getName().toUpperCase());
                // Entries added by an upgrade transform will not have a date
                final Date date = entry.getValue().equals(useCurrentDatetimeSignal) ? new Date()
                        : entry.getValue().getTime();
                convertedEntries.put(new IPortletLifecycleEntry() {
                    @Override
                    public int getUserId() {
                        return user.getID();
                    }

                    @Override
                    public PortletLifecycleState getLifecycleState() {
                        return state;
                    }

                    @Override
                    public Date getDate() {
                        return date;
                    }

                    @Override
                    public int compareTo(IPortletLifecycleEntry o) {
                        int rslt = date.compareTo(o.getDate());
                        if (rslt == 0) {
                            rslt = state.getOrder() - o.getLifecycleState().getOrder();
                        }
                        return rslt;
                    }
                }, user);
            }
            /*
             * Apply them to the portlet definition
             */
            convertedEntries.forEach((k, v) -> {
                portletDefinition.updateLifecycleState(k.getLifecycleState(), v, k.getDate());
            });
        }
    }
}