Package org.gatein.wsrp.consumer

Source Code of org.gatein.wsrp.consumer.ProducerInfo

/*
* 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.consumer;

import org.gatein.common.NotYetImplemented;
import org.gatein.common.util.ParameterValidation;
import org.gatein.pc.api.InvokerUnavailableException;
import org.gatein.pc.api.NoSuchPortletException;
import org.gatein.pc.api.Portlet;
import org.gatein.pc.api.PortletContext;
import org.gatein.pc.api.PortletInvokerException;
import org.gatein.pc.api.info.EventInfo;
import org.gatein.pc.api.info.TypeInfo;
import org.gatein.wsrp.WSRPConstants;
import org.gatein.wsrp.WSRPTypeFactory;
import org.gatein.wsrp.WSRPUtils;
import org.gatein.wsrp.consumer.portlet.WSRPPortlet;
import org.gatein.wsrp.consumer.portlet.info.WSRPEventInfo;
import org.gatein.wsrp.consumer.portlet.info.WSRPPortletInfo;
import org.gatein.wsrp.consumer.spi.ConsumerRegistrySPI;
import org.gatein.wsrp.servlet.UserAccess;
import org.gatein.wsrp.spec.v2.WSRP2Constants;
import org.oasis.wsrp.v2.CookieProtocol;
import org.oasis.wsrp.v2.EventDescription;
import org.oasis.wsrp.v2.ExportDescription;
import org.oasis.wsrp.v2.Extension;
import org.oasis.wsrp.v2.ExtensionDescription;
import org.oasis.wsrp.v2.InvalidHandle;
import org.oasis.wsrp.v2.InvalidRegistration;
import org.oasis.wsrp.v2.ItemDescription;
import org.oasis.wsrp.v2.Lifetime;
import org.oasis.wsrp.v2.ModelDescription;
import org.oasis.wsrp.v2.ModelTypes;
import org.oasis.wsrp.v2.ModifyRegistrationRequired;
import org.oasis.wsrp.v2.OperationFailed;
import org.oasis.wsrp.v2.PortletDescription;
import org.oasis.wsrp.v2.PortletPropertyDescriptionResponse;
import org.oasis.wsrp.v2.RegistrationContext;
import org.oasis.wsrp.v2.RegistrationData;
import org.oasis.wsrp.v2.ResourceList;
import org.oasis.wsrp.v2.ServiceDescription;
import org.oasis.wsrp.v2.WSRPV2PortletManagementPortType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import javax.xml.ws.Holder;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
* @version $Revision: 12692 $
* @since 2.6
*/
public class ProducerInfo
{
   private final static Logger log = LoggerFactory.getLogger(ProducerInfo.class);
   private final static boolean debug = log.isDebugEnabled();
   public static final Integer DEFAULT_CACHE_VALUE = 300;

   // Persistent information

   /** persistence key */
   private String key;

   /** Configuration of the remote WS endpoints */
   private EndpointConfigurationInfo persistentEndpointInfo;

   /** Registration information */
   private RegistrationInfo persistentRegistrationInfo;

   /** The Producer's identifier */
   private String persistentId;

   /** The cache expiration duration (in seconds) for cached values */
   private Integer persistentExpirationCacheSeconds = DEFAULT_CACHE_VALUE;

   /** The activated status of the associated Consumer */
   private boolean persistentActive;

   // GTNWSRP-239: information that's currently transient but should probably be persistent
   /**
    * GTNWSRP-239: whether or not this ProducerInfo requires ModifyRegistration to be called, currently persisted via
    * mixin
    */
   private boolean isModifyRegistrationRequired;

   /** GTNWSRP-239: last modification epoch: currently persistent via mixin */
   private long lastModified;

   // Transient information

   /** The Cookie handling policy required by the Producer */
   private transient CookieProtocol requiresInitCookie;

   /** The Producer-Offered Portlets (handle -> WSRPPortlet) */
   private transient Map<String, Portlet> popsMap;

   /** A cache for Consumer-Configured Portlets (handle -> WSRPPortlet) */
   private transient Map<String, Portlet> ccpsMap;

   /** Portlet groups. */
   private transient Map<String, Set<Portlet>> portletGroups;

   /** Time at which the cache expires */
   private transient long expirationTimeMillis;

   private transient final ConsumerRegistrySPI registry;
   private static final String ERASED_LOCAL_REGISTRATION_INFORMATION = "Erased local registration information!";

   private transient RegistrationInfo expectedRegistrationInfo;

   private transient Map<String, ItemDescription> customModes;
   private transient Map<String, ItemDescription> customWindowStates;

   /** Events */
   private transient Map<QName, EventInfo> eventDescriptions;

   /** Supported options */
   private transient Set<String> supportedOptions = Collections.emptySet();

   /*protected org.oasis.wsrp.v1.ItemDescription[] userCategoryDescriptions;
   protected org.oasis.wsrp.v1.ItemDescription[] customUserProfileItemDescriptions;  

   protected java.lang.String[] locales;
   protected org.oasis.wsrp.v1.ResourceList resourceList;*/


   public ProducerInfo(ConsumerRegistrySPI consumerRegistry)
   {
      persistentEndpointInfo = new EndpointConfigurationInfo();
      persistentRegistrationInfo = RegistrationInfo.createUndeterminedRegistration(this);
      this.registry = consumerRegistry;
   }

   @Override
   public boolean equals(Object o)
   {
      if (this == o)
      {
         return true;
      }
      if (o == null || getClass() != o.getClass())
      {
         return false;
      }

      ProducerInfo that = (ProducerInfo)o;

      if (key != null ? !key.equals(that.key) : that.key != null)
      {
         return false;
      }
      if (!persistentId.equals(that.persistentId))
      {
         return false;
      }

      return true;
   }

   @Override
   public int hashCode()
   {
      int result = key != null ? key.hashCode() : 0;
      result = 31 * result + persistentId.hashCode();
      return result;
   }

   @Override
   public String toString()
   {
      final StringBuilder sb = new StringBuilder();
      sb.append("ProducerInfo");
      sb.append("{key='").append(key).append('\'');
      sb.append(", id='").append(persistentId).append('\'');
      sb.append('}');
      return sb.toString();
   }

   public ConsumerRegistrySPI getRegistry()
   {
      return registry;
   }

   public String getKey()
   {
      return key;
   }

   public void setKey(String key)
   {
      this.key = key;
   }

   public Set<String> getSupportedCustomModes()
   {
      if (customModes == null)
      {
         return Collections.emptySet();
      }
      return Collections.unmodifiableSet(customModes.keySet());
   }

   public Set<String> getSupportedCustomWindowStates()
   {
      if (customWindowStates == null)
      {
         return Collections.emptySet();
      }
      return Collections.unmodifiableSet(customWindowStates.keySet());
   }

   public EndpointConfigurationInfo getEndpointConfigurationInfo()
   {
      return persistentEndpointInfo;
   }

   public void setEndpointConfigurationInfo(EndpointConfigurationInfo endpointConfigurationInfo)
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(endpointConfigurationInfo, "EndpointConfigurationInfo");
      this.persistentEndpointInfo = endpointConfigurationInfo;
   }

   public RegistrationInfo getRegistrationInfo()
   {
      // update parent since it might not be set when unfrozen from persistence
      persistentRegistrationInfo.setParent(this);
      return persistentRegistrationInfo;
   }

   public void setRegistrationInfo(RegistrationInfo registrationInfo)
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(registrationInfo, "RegistrationInfo");
      this.persistentRegistrationInfo = registrationInfo;
   }

   public boolean isRegistered()
   {
      return persistentRegistrationInfo.isRegistered();
   }

   public boolean isRegistrationRequired()
   {
      return persistentRegistrationInfo.isRegistrationDeterminedRequired();
   }

   public boolean isRegistrationChecked()
   {
      return persistentRegistrationInfo.isRegistrationRequired() != null;
   }

   public boolean hasLocalRegistrationInfo()
   {
      return persistentRegistrationInfo.hasLocalInfo();
   }

   /**
    * Determines whether the associated consumer is active.
    *
    * @return
    */
   public boolean isActive()
   {
      return persistentActive/* && persistentEndpointInfo.isAvailable()*/;
   }

   /**
    * Activates or de-activate this Consumer. Note that this shouldn't be called directly as ConsumersRegistry will
    * handle activation.
    *
    * @param active
    */
   public void setActive(boolean active)
   {
      this.persistentActive = active;
   }

   public void setActiveAndSave(boolean active)
   {
      setActive(active);
      registry.updateProducerInfo(this);
   }

   public boolean isModifyRegistrationRequired()
   {
      return isModifyRegistrationRequired || persistentRegistrationInfo.isModifyRegistrationNeeded();
   }

   // FIX-ME: remove when a better dirty management is in place at property level

   public void setModifyRegistrationRequired(boolean modifyRegistrationRequired)
   {
      this.isModifyRegistrationRequired = modifyRegistrationRequired;
   }

   public CookieProtocol getRequiresInitCookie()
   {
      return requiresInitCookie;
   }

   public RegistrationInfo getExpectedRegistrationInfo()
   {
      return expectedRegistrationInfo;
   }

   /**
    * Refreshes the producer's information from the service description if required.
    *
    * @param forceRefresh whether or not to force a refresh regardless of whether one would have been required based on
    *                     cache expiration
    * @return <code>true</code> if the producer's information was just refreshed, <code>false</code> otherwise
    * @throws PortletInvokerException if registration was required but couldn't be achieved properly
    */
   public boolean refresh(boolean forceRefresh) throws PortletInvokerException
   {
      return detailedRefresh(forceRefresh).didRefreshHappen();
   }

   public RefreshResult detailedRefresh(boolean forceRefresh) throws PortletInvokerException
   {
      RefreshResult result = internalRefresh(forceRefresh);

      // if the refresh failed, return immediately
      if (RefreshResult.Status.FAILURE.equals(result.getStatus()))
      {
         setActiveAndSave(false);
         return result;
      }

      // update DB
      if (result.didRefreshHappen())
      {
         // mark as inactive if the refresh had issues...
         if (result.hasIssues())
         {
            setActive(false);

            // record what the Producer's expectations are if we managed to get a service description
            expectedRegistrationInfo = new RegistrationInfo(this.persistentRegistrationInfo);
            expectedRegistrationInfo.refresh(result.getServiceDescription(), getId(), true, true, true);
         }
         else
         {
            // mark as active if it wasn't already
            if (!isActive())
            {
               setActive(true);
            }

            // if we didn't have any issues, then the expected registration info is the one we have
            expectedRegistrationInfo = persistentRegistrationInfo;
         }

         registry.updateProducerInfo(this);
      }

      return result;
   }

   private RefreshResult internalRefresh(boolean forceRefresh) throws PortletInvokerException
   {
      ServiceDescription serviceDescription;

      if (isModifyRegistrationRequired())
      {
         return new RefreshResult(RefreshResult.Status.MODIFY_REGISTRATION_REQUIRED);
      }

      // might neeed a different cache value: right now, we cache the whole producer info but we might want to cache
      // POPs and rest of producer info separetely...
      if (forceRefresh || isRefreshNeeded(true))
      {
         log.debug("Refreshing info for producer '" + getId() + "'");


         RefreshResult result = new RefreshResult(); // success by default!

         try
         {
            persistentEndpointInfo.refresh();
         }
         catch (InvokerUnavailableException e)
         {
            log.debug("Couldn't refresh endpoint information, attempting a second time: " + e, e);

            // try again as refresh on a failed service factory will fail without attempting the refresh
            try
            {
               persistentEndpointInfo.forceRefresh();
            }
            catch (InvokerUnavailableException e1)
            {
               result.setStatus(RefreshResult.Status.FAILURE);
               return result;
            }
         }

         // get the service description from the producer
         try
         {
            // if we don't yet have registration information, get an unregistered service description
            serviceDescription = getUnmanagedServiceDescription(persistentRegistrationInfo.isUndetermined());
            result.setServiceDescription(serviceDescription);
         }
         catch (OperationFailed operationFailedFault)
         {
            // if we have local registration info, the OperationFailedFault might indicate a need to call modifyRegistration
            if (hasLocalRegistrationInfo())
            {
               log.debug("OperationFailedFault occurred, might indicate a need to modify registration", operationFailedFault);

               return handleModifyRegistrationNeeded(result);
            }
            else
            {
               serviceDescription = rethrowAsInvokerUnvailable(operationFailedFault);
            }
         }
         catch (InvalidRegistration invalidRegistrationFault)
         {
            log.debug("InvalidRegistrationFault occurred", invalidRegistrationFault);

            // attempt to get unregistered service description
            serviceDescription = getServiceDescription(true);
            result.setServiceDescription(serviceDescription);

            // check our registration information against what is sent in the service description
            RefreshResult registrationResult = internalRefreshRegistration(serviceDescription, false, true, true);
            if (registrationResult.hasIssues())
            {
               setActiveAndSave(false);
               rethrowAsInvokerUnvailable(invalidRegistrationFault);
            }

            return refreshInfo(false, serviceDescription, result);
         }
         catch (ModifyRegistrationRequired modifyRegistrationRequired)
         {
            return handleModifyRegistrationNeeded(result);
         }

         return refreshInfo(forceRefresh, serviceDescription, result);
      }

      return new RefreshResult(RefreshResult.Status.BYPASSED);
   }

   private RefreshResult handleModifyRegistrationNeeded(RefreshResult result) throws PortletInvokerException
   {
      ServiceDescription serviceDescription;// attempt to get unregistered service description
      serviceDescription = getServiceDescription(true);
      result.setServiceDescription(serviceDescription);

      // re-validate the registration information
      RefreshResult registrationResult = internalRefreshRegistration(serviceDescription, false, true, true);
      if (registrationResult.hasIssues())
      {
         // if the registration validation has issues, we need to modify our local information
         setModifyRegistrationRequired(true);
         setActive(false);
      }
      else
      {
         // we might be in a situation where the producer changed the registration back to the initial state
         // which is, granted, pretty rare... attempt modifyRegistration
         log.debug("modifyRegistration was called after OperationFailedFault when a check of registration data didn't reveal any issue...");
         modifyRegistration(true);
      }

      result.setRegistrationResult(registrationResult);
      return result;
   }

   private RefreshResult refreshInfo(boolean forceRefresh, ServiceDescription serviceDescription, RefreshResult result)
      throws PortletInvokerException
   {
      // do we need to call initCookie or not?
      requiresInitCookie = serviceDescription.getRequiresInitCookie();
      log.debug("Requires initCookie: " + requiresInitCookie);

      // supported options
      final List<String> supportedOptions = serviceDescription.getSupportedOptions();
      if (ParameterValidation.existsAndIsNotEmpty(supportedOptions))
      {
         this.supportedOptions = new HashSet<String>(supportedOptions);
      }

      // custom mode descriptions
      customModes = toMap(serviceDescription.getCustomModeDescriptions());

      // custom window state descriptions
      customWindowStates = toMap(serviceDescription.getCustomWindowStateDescriptions());

      // event descriptions
      List<EventDescription> eventDescriptions = serviceDescription.getEventDescriptions();
      if (!eventDescriptions.isEmpty())
      {
         this.eventDescriptions = new HashMap<QName, EventInfo>(eventDescriptions.size());

         for (final EventDescription event : eventDescriptions)
         {
            QName name = event.getName();
            EventInfo eventInfo = new WSRPEventInfo(
               name,
               WSRPUtils.convertToCommonLocalizedStringOrNull(event.getLabel()),
               WSRPUtils.convertToCommonLocalizedStringOrNull(event.getDescription()),
               new TypeInfo()
               {
                  public String getName()
                  {
                     return event.getType().toString();
                  }

                  public XmlRootElement getXMLBinding()
                  {
                     throw new NotYetImplemented(); // todo
                  }
               },
               event.getAliases());

            this.eventDescriptions.put(name, eventInfo);
         }
      }

      // do we need to register?
      if (serviceDescription.isRequiresRegistration())
      {
         // refresh and force check for extra props if the registered SD failed
         // todo: deal with forcing check of extra registration properties properly (if needed)
         RefreshResult registrationResult = internalRefreshRegistration(serviceDescription, true, forceRefresh, false);

         // attempt to register and determine if the current service description can be used to extract POPs
         if (!registrationResult.hasIssues())
         {
            registrationResult = register(serviceDescription, false);
            if (!registrationResult.hasIssues())
            {
               // registration occurred, so we should ask for a new service description
               serviceDescription = getServiceDescription(false);
            }

            // extract the POPs
            extractOfferedPortlets(serviceDescription);
         }

         result.setRegistrationResult(registrationResult);

         return result;
      }
      else
      {
         log.debug("Registration not required");
         persistentRegistrationInfo = new RegistrationInfo(this, false);
         extractOfferedPortlets(serviceDescription);
         return result;
      }
   }

   private Map<String, ItemDescription> toMap(List<ItemDescription> itemDescriptions)
   {
      if (itemDescriptions == null)
      {
         return null;
      }
      else
      {
         Map<String, ItemDescription> result = new HashMap<String, ItemDescription>(itemDescriptions.size());
         for (ItemDescription itemDescription : itemDescriptions)
         {
            result.put(itemDescription.getItemName(), itemDescription);
         }
         return result;
      }
   }

   public String getId()
   {
      return persistentId;
   }

   public void setId(String id)
   {
      this.persistentId = id;
   }

   /**
    * Extracts a map of offered Portlet objects from ServiceDescription
    *
    * @param sd
    * @return a Map (portlet handle -> Portlet) of the offered portlets.
    */
   private Map extractOfferedPortlets(ServiceDescription sd)
   {
      if (sd == null)
      {
         throw new IllegalArgumentException("Provided ServiceDescription can't be null");
      }

      List<PortletDescription> portletDescriptions = sd.getOfferedPortlets();

      if (portletDescriptions != null)
      {
         int length = portletDescriptions.size();
         log.debug("Extracting " + length + " portlets.");
         popsMap = new LinkedHashMap<String, Portlet>(length);
         portletGroups = new HashMap<String, Set<Portlet>>();

         for (PortletDescription portletDescription : portletDescriptions)
         {
            WSRPPortlet wsrpPortlet = createWSRPPortletFromPortletDescription(portletDescription);

            if (wsrpPortlet != null)
            {
               popsMap.put(wsrpPortlet.getContext().getId(), wsrpPortlet);
            }
         }
      }
      else
      {
         popsMap = Collections.emptyMap();
         portletGroups = Collections.emptyMap();
      }

      //todo: could extract more information here... and rename method more appropriately
      resetCacheTimerIfNeeded();

      return popsMap;
   }

   /**
    * @param portletDescription
    * @return
    * @since 2.6
    */
   WSRPPortlet createWSRPPortletFromPortletDescription(PortletDescription portletDescription)
   {
      ParameterValidation.throwIllegalArgExceptionIfNull(portletDescription, "PortletDescription");
      String portletHandle = portletDescription.getPortletHandle();
      log.debug("Extracting info for '" + portletHandle + "' portlet");
      WSRPPortletInfo info = new WSRPPortletInfo(portletDescription, this);
      WSRPPortlet wsrpPortlet = null;
      if (info.isUsesMethodGet())
      {
         log.warn("Portlet '" + portletHandle
            + "' uses the GET method in forms. Since we don't handle this, this portlet will be excluded from " +
            "the list of offered portlets for producer " + persistentId);
      }
      else
      {
         if (info.isHasUserSpecificState())
         {
            log.debug("Portlet '" + portletHandle + "' will store persistent state for each user.");
         }

         wsrpPortlet = new WSRPPortlet(PortletContext.createPortletContext(portletHandle, false), info);

         // add the portlet to the appropriate group if needed
         String portletGroupId = portletDescription.getGroupID();
         if (portletGroupId != null)
         {
            Set<Portlet> groupedPortlets = portletGroups.get(portletGroupId);
            if (groupedPortlets == null)
            {
               groupedPortlets = new HashSet<Portlet>();
               portletGroups.put(portletGroupId, groupedPortlets);
            }
            groupedPortlets.add(wsrpPortlet);
         }
      }
      return wsrpPortlet;
   }

   public Portlet getPortlet(PortletContext portletContext) throws PortletInvokerException
   {
      String portletHandle = portletContext.getId();
      ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(portletHandle, "Portlet handle", "getPortlet");
      log.debug("Retrieving portlet '" + portletHandle + "'");

      // check if we need to refresh
      boolean justRefreshed = refresh(false);

      // First try caches if caches are still valid or we just refreshed
      Portlet portlet = getPortletFromCaches(portletHandle, justRefreshed);

      if (portlet != null) // we had a match in cache, return it
      {
         log.debug("Portlet was cached");
         return portlet;
      }
      else // otherwise, retrieve just the information for the appropriate portlet
      {
         log.debug("Trying to retrieve portlet via getPortletDescription");

         try
         {
            Holder<PortletDescription> descriptionHolder = new Holder<PortletDescription>();
            persistentEndpointInfo.getPortletManagementService().getPortletDescription(
               getRegistrationContext(),
               WSRPUtils.convertToWSRPPortletContext(portletContext),
               UserAccess.getUserContext(),
               WSRPConstants.getDefaultLocales(), // todo: deal with locales better
               descriptionHolder,
               new Holder<ResourceList>(),
               new Holder<List<Extension>>());
            portlet = createWSRPPortletFromPortletDescription(descriptionHolder.value);

            // add the portlet to the CCP cache
            if (ccpsMap == null)
            {
               ccpsMap = new HashMap<String, Portlet>();
            }
            ccpsMap.put(portletHandle, portlet);

            return portlet;
         }
         catch (InvalidHandle invalidHandleFault)
         {
            throw new NoSuchPortletException(invalidHandleFault, portletHandle);
         }
         catch (Exception e)
         {
            log.debug("Couldn't get portlet via getPortletDescription for producer '" + persistentId
               + "'. Attempting to retrieve it from the service description as this producer might not support the PortletManagement interface.", e);

            justRefreshed = refresh(true);
            portlet = getPortletFromCaches(portletHandle, justRefreshed);

            if (portlet == null)
            {
               throw new NoSuchPortletException(portletHandle);
            }
            else
            {
               return portlet;
            }
         }
      }
   }

   private Portlet getPortletFromCaches(String portletHandle, boolean justRefreshed)
   {
      Portlet portlet = null;

      if (justRefreshed || (useCache() && !isCacheExpired()))
      {
         log.debug("Trying cached POPs");

         portlet = popsMap.get(portletHandle);

         if (portlet == null && ccpsMap != null)
         {
            log.debug("Trying cached CCPs");
            portlet = ccpsMap.get(portletHandle);
         }
      }
      return portlet;
   }

   Map<String, Set<Portlet>> getPortletGroupMap() throws PortletInvokerException
   {
      return portletGroups;
   }

   public Map<String, Portlet> getPortletMap() throws PortletInvokerException
   {
      refresh(false);
      return popsMap;
   }

   // Cache support ****************************************************************************************************

   private boolean useCache()
   {
      return persistentExpirationCacheSeconds != null && persistentExpirationCacheSeconds > 0;
   }

   private void resetCacheTimerIfNeeded()
   {
      expirationTimeMillis = System.currentTimeMillis() + (getSafeExpirationCacheSeconds() * 1000);
   }

   /**
    * @return
    * @since 2.6
    */
   private boolean isCacheExpired()
   {
      boolean result = !useCache() || System.currentTimeMillis() > expirationTimeMillis || popsMap == null
         || portletGroups == null;
      if (result)
      {
         log.debug("Cache expired or not used");
      }
      return result;
   }

   public Integer getExpirationCacheSeconds()
   {
      return persistentExpirationCacheSeconds;
   }

   public void setExpirationCacheSeconds(Integer expirationCacheSeconds)
   {
      // record the previous cache expiration duration
      Integer previousMS = getSafeExpirationCacheSeconds() * 1000;

      // assign the new value
      this.persistentExpirationCacheSeconds = expirationCacheSeconds;

      // recompute the expiration time based on previous value and new one
      long lastExpirationTimeChange = expirationTimeMillis - previousMS;
      int newMS = getSafeExpirationCacheSeconds() * 1000;
      if (lastExpirationTimeChange > 0)
      {
         expirationTimeMillis = lastExpirationTimeChange + newMS;
      }
      else
      {
         expirationTimeMillis = System.currentTimeMillis();
      }

   }

   /**
    * Returns the cache expiration duration in seconds as a positive value or zero so that it's safe to use in cache
    * expiration time computations.
    *
    * @return
    */
   private int getSafeExpirationCacheSeconds()
   {
      return useCache() ? persistentExpirationCacheSeconds : 0;
   }

   private ServiceDescription getUnmanagedServiceDescription(boolean asUnregistered) throws PortletInvokerException, OperationFailed, InvalidRegistration, ModifyRegistrationRequired
   {
      //todo: might need to implement customization of default service description
      ServiceDescription serviceDescription;
      try
      {
         Holder<Boolean> requiresRegistration = new Holder<Boolean>();
         Holder<List<PortletDescription>> offeredPortlets = new Holder<List<PortletDescription>>();
         Holder<List<ItemDescription>> userCategoryDescriptions = new Holder<List<ItemDescription>>();
         Holder<List<ItemDescription>> windowStateDescriptions = new Holder<List<ItemDescription>>();
         Holder<List<ItemDescription>> modeDescriptions = new Holder<List<ItemDescription>>();
         Holder<CookieProtocol> requiresInitCookie = new Holder<CookieProtocol>();
         Holder<ModelDescription> registrationPropertyDescription = new Holder<ModelDescription>();
         Holder<List<String>> locales = new Holder<List<String>>();
         Holder<ResourceList> resourceList = new Holder<ResourceList>();
         Holder<List<EventDescription>> eventDescriptions = new Holder<List<EventDescription>>();
         Holder<ModelTypes> schemaTypes = new Holder<ModelTypes>();
         Holder<List<String>> supportedOptions = new Holder<List<String>>();
         Holder<ExportDescription> exportDescription = new Holder<ExportDescription>();
         Holder<Boolean> mayReturnRegistrationState = new Holder<Boolean>();
         final Holder<List<ExtensionDescription>> extensionDescriptions = new Holder<List<ExtensionDescription>>();
         final Holder<List<Extension>> extensions = new Holder<List<Extension>>();

         // invocation
         persistentEndpointInfo.getServiceDescriptionService().getServiceDescription(
            asUnregistered ? null : getRegistrationContext(),
            WSRPConstants.getDefaultLocales(), // todo: deal with locales better
            null, // todo: provide a way to only request info on some portlets?
            UserAccess.getUserContext(),
            requiresRegistration,
            offeredPortlets,
            userCategoryDescriptions,
            extensionDescriptions,
            windowStateDescriptions,
            modeDescriptions,
            requiresInitCookie,
            registrationPropertyDescription,
            locales,
            resourceList,
            eventDescriptions,
            schemaTypes,
            supportedOptions,
            exportDescription,
            mayReturnRegistrationState,
            extensions);

         // TODO: fix-me
         serviceDescription = WSRPTypeFactory.createServiceDescription(requiresRegistration.value);
         serviceDescription.setRegistrationPropertyDescription(registrationPropertyDescription.value);
         serviceDescription.setRequiresInitCookie(requiresInitCookie.value);
         serviceDescription.setResourceList(resourceList.value);
         serviceDescription.setSchemaType(schemaTypes.value);
         serviceDescription.setExportDescription(exportDescription.value);
         serviceDescription.setMayReturnRegistrationState(mayReturnRegistrationState.value);

         if (ParameterValidation.existsAndIsNotEmpty(modeDescriptions.value))
         {
            serviceDescription.getCustomModeDescriptions().addAll(modeDescriptions.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(windowStateDescriptions.value))
         {
            serviceDescription.getCustomWindowStateDescriptions().addAll(windowStateDescriptions.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(locales.value))
         {
            serviceDescription.getLocales().addAll(locales.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(offeredPortlets.value))
         {
            serviceDescription.getOfferedPortlets().addAll(offeredPortlets.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(userCategoryDescriptions.value))
         {
            serviceDescription.getUserCategoryDescriptions().addAll(userCategoryDescriptions.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(eventDescriptions.value))
         {
            serviceDescription.getEventDescriptions().addAll(eventDescriptions.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(extensionDescriptions.value))
         {
            serviceDescription.getExtensionDescriptions().addAll(extensionDescriptions.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(extensions.value))
         {
            serviceDescription.getExtensions().addAll(extensions.value);
         }
         if (ParameterValidation.existsAndIsNotEmpty(supportedOptions.value))
         {
            serviceDescription.getSupportedOptions().addAll(supportedOptions.value);
         }

         return serviceDescription;
      }
      catch (Exception e)
      {
         log.debug("Caught Exception in getServiceDescription:\n", e);

         // de-activate
         setActiveAndSave(false);

         if (e instanceof InvalidRegistration)
         {
            resetRegistration();

            throw (InvalidRegistration)e;
         }
         else if (e instanceof OperationFailed)
         {
            throw (OperationFailed)e; // rethrow to deal at higher level as meaning can vary depending on context
         }
         else if (e instanceof ModifyRegistrationRequired)
         {
            throw (ModifyRegistrationRequired)e;
         }

         return rethrowAsInvokerUnvailable(e);
      }
   }

   ServiceDescription getServiceDescription(boolean asUnregistered) throws PortletInvokerException
   {
      try
      {
         return getUnmanagedServiceDescription(asUnregistered);
      }
      catch (OperationFailed operationFailedFault)
      {
         return rethrowAsInvokerUnvailable(operationFailedFault);
      }
      catch (InvalidRegistration invalidRegistrationFault)
      {
         return rethrowAsInvokerUnvailable(invalidRegistrationFault);
      }
      catch (ModifyRegistrationRequired modifyRegistrationRequired)
      {
         return rethrowAsInvokerUnvailable(modifyRegistrationRequired);
      }
   }

   private ServiceDescription rethrowAsInvokerUnvailable(Exception e) throws InvokerUnavailableException
   {
      Throwable cause = e.getCause();
      throw new InvokerUnavailableException("Problem getting service description for producer "
         + persistentId + ", please see the logs for more information. ", cause == null ? e : cause);
   }

   public RegistrationContext getRegistrationContext() throws PortletInvokerException
   {
      if (persistentRegistrationInfo.isUndetermined())
      {
         refresh(false);
      }

      return persistentRegistrationInfo.getRegistrationContext();
   }

   public void resetRegistration() throws PortletInvokerException
   {
      persistentRegistrationInfo.resetRegistration();

      invalidateCache();
      registry.updateProducerInfo(this);
   }

   // make package only after package reorg

   public PortletPropertyDescriptionResponse getPropertyDescriptionsFor(String portletHandle)
   {
      ParameterValidation.throwIllegalArgExceptionIfNullOrEmpty(portletHandle, "portlet handle", null);
      try
      {
         WSRPV2PortletManagementPortType service = getEndpointConfigurationInfo().getPortletManagementService();

         Holder<ModelDescription> modelDescription = new Holder<ModelDescription>();
         Holder<ResourceList> resourceList = new Holder<ResourceList>();
         service.getPortletPropertyDescription(
            getRegistrationContext(),
            WSRPTypeFactory.createPortletContext(portletHandle),
            UserAccess.getUserContext(),
            WSRPConstants.getDefaultLocales(),
            modelDescription,
            resourceList,
            new Holder<List<Extension>>());

         PortletPropertyDescriptionResponse response = WSRPTypeFactory.createPortletPropertyDescriptionResponse(null);
         response.setModelDescription(modelDescription.value);
         response.setResourceList(resourceList.value);

         return response;
      }
      catch (InvalidHandle invalidHandleFault)
      {
         throw new IllegalArgumentException("Unknown portlet '" + portletHandle + "'");
      }
      catch (InvalidRegistration invalidRegistrationFault)
      {
         try
         {
            resetRegistration();
         }
         catch (PortletInvokerException e)
         {
            throw new RuntimeException("Couldn't reset registration", e);
         }
         throw new IllegalArgumentException("Couldn't get property descriptions for portlet '" + portletHandle
            + "' because the provided registration is invalid!");
      }
      catch (Exception e)
      {
         // if we receive an exception that we cannot handle, since the support for PortletManagement is optional,
         // just return null as if the portlet had no properties
         log.debug("Couldn't get property descriptions for portlet '" + portletHandle + "'", e);
         return null;
      }
   }

   public void register() throws PortletInvokerException
   {
      try
      {
         register(null, false);
      }
      catch (PortletInvokerException e)
      {
         registry.updateProducerInfo(this);
         throw e;
      }
   }

   /**
    * Attempts to register with the producer.
    *
    * @param serviceDescription
    * @param forceRefresh
    * @return <code>true</code> if the client code should ask for a new service description, <code>false</code> if the
    *         specified description is good to be further processed
    * @throws PortletInvokerException
    * @since 2.6
    */
   private RefreshResult register(ServiceDescription serviceDescription, boolean forceRefresh) throws PortletInvokerException
   {
      if (!isRegistered())
      {
         persistentEndpointInfo.refresh();

         if (serviceDescription == null)
         {
            serviceDescription = getServiceDescription(false);
         }

         if (serviceDescription.isRequiresRegistration())
         {
            // check if the configured registration information is correct and if we can get the service description
            RefreshResult result = persistentRegistrationInfo.refresh(serviceDescription, persistentId, true, forceRefresh, false);
            if (!result.hasIssues())
            {
               try
               {
                  log.debug("Attempting registration");
                  RegistrationData registrationData = persistentRegistrationInfo.getRegistrationData();
                  Holder<String> registrationHandle = new Holder<String>();
                  Holder<byte[]> registrationState = new Holder<byte[]>();

                  // invocation
                  persistentEndpointInfo.getRegistrationService().register(
                     registrationData,
                     null, // todo: support leasing?
                     UserAccess.getUserContext(),
                     registrationState,
                     new Holder<Lifetime>(),
                     new Holder<List<Extension>>(),
                     registrationHandle
                  );

                  RegistrationContext registrationContext = WSRPTypeFactory.createRegistrationContext(registrationHandle.value);
                  registrationContext.setRegistrationState(registrationState.value);

                  persistentRegistrationInfo.setRegistrationContext(registrationContext);

                  if (debug)
                  {
                     String msg = "Consumer with id '" + persistentId + "' successfully registered with handle: '"
                        + registrationContext.getRegistrationHandle() + "'";
                     log.debug(msg);
                  }

                  RefreshResult res = new RefreshResult();

                  res.setRegistrationResult(result);
                  return res;
               }
               catch (Exception e)
               {
                  persistentRegistrationInfo.resetRegistration();
                  setActive(false);
                  throw new PortletInvokerException("Couldn't register with producer '" + persistentId + "'", e);
               }
            }
            else
            {
               log.debug(result.getStatus().toString());
               setActive(false);
               throw new PortletInvokerException("Consumer is not ready to be registered with producer because of missing or invalid registration information.");
            }
         }
      }

      return new RefreshResult(RefreshResult.Status.BYPASSED);
   }

   public void deregister() throws PortletInvokerException
   {
      if (isRegistered())
      {
         persistentEndpointInfo.refresh();

         try
         {
            RegistrationContext registrationContext = getRegistrationContext();
            persistentEndpointInfo.getRegistrationService().deregister(registrationContext, UserAccess.getUserContext());
            log.info("Consumer with id '" + persistentId + "' deregistered.");
         }
         catch (Exception e)
         {
            throw new PortletInvokerException("Couldn't deregister with producer '" + persistentId + "'", e);
         }
         finally
         {
            resetRegistration();
         }
      }
      else
      {
         throw new IllegalStateException("Cannot deregister producer '" + persistentId + "' as it's not registered");
      }

   }

   public void modifyRegistration() throws PortletInvokerException
   {
      try
      {
         modifyRegistration(false);
      }
      finally
      {
         registry.updateProducerInfo(this);
      }
   }

   private void modifyRegistration(boolean force) throws PortletInvokerException
   {
      if (persistentRegistrationInfo.getRegistrationHandle() != null)
      {
         persistentEndpointInfo.refresh();

         if (force || isModifyRegistrationRequired())
         {
            try
            {
               RegistrationContext registrationContext = getRegistrationContext();
               Holder<byte[]> registrationState = new Holder<byte[]>();

               // invocation
               persistentEndpointInfo.getRegistrationService().modifyRegistration(
                  registrationContext,
                  persistentRegistrationInfo.getRegistrationData(),
                  UserAccess.getUserContext(),
                  registrationState,
                  new Holder<Lifetime>(),
                  new Holder<List<Extension>>());

               // force refresh of internal RegistrationInfo state
               persistentRegistrationInfo.setRegistrationValidInternalState();

               // registration is not modified anymore :)
               setModifyRegistrationRequired(false);

               // update state
               persistentRegistrationInfo.setRegistrationState(registrationState.value);

               log.info("Consumer with id '" + persistentId + "' sucessfully modified its registration.");

               // reset cache to be able to see new offered portlets on the next refresh
               invalidateCache();
            }
            catch (Exception e)
            {
               throw new PortletInvokerException("Couldn't modify registration with producer '" + persistentId + "'", e);
            }
         }
      }
      else
      {
         throw new IllegalStateException("Cannot modify registration for producer '" + persistentId
            + "' as it's not registered");
      }
   }

   private void invalidateCache()
   {
      if (useCache())
      {
         expirationTimeMillis = System.currentTimeMillis();
      }
   }

   private RefreshResult internalRefreshRegistration(ServiceDescription serviceDescription, boolean mergeWithLocalInfo, boolean forceRefresh, boolean forceCheckOfExtraProps) throws PortletInvokerException
   {
      RefreshResult result =
         persistentRegistrationInfo.refresh(serviceDescription, persistentId, mergeWithLocalInfo, forceRefresh, forceCheckOfExtraProps);

      log.debug("Refreshed registration information for consumer with id '" + persistentId + "'");

      return result;
   }

   public boolean isRefreshNeeded(boolean considerCache)
   {
      boolean result = (considerCache && isCacheExpired())
         || persistentRegistrationInfo.isRefreshNeeded()
         || persistentEndpointInfo.isRefreshNeeded();
      if (result)
      {
         log.debug("Refresh needed for producer '" + persistentId + "'");
      }
      return result;
   }

   void removeHandleFromCaches(String portletHandle)
   {
      log.debug("Removing '" + portletHandle + "' from caches.");
      ccpsMap.remove(portletHandle);
      popsMap.remove(portletHandle);
   }

   public void eraseRegistrationInfo()
   {
      persistentRegistrationInfo = RegistrationInfo.createUndeterminedRegistration(this);

      registry.updateProducerInfo(this);

      log.warn(ERASED_LOCAL_REGISTRATION_INFORMATION);
   }

   public EventInfo getInfoForEvent(QName name)
   {
      if (eventDescriptions == null)
      {
         return null;
      }
      else
      {
         return eventDescriptions.get(name);
      }
   }

   public long getLastModified()
   {
      return lastModified;
   }

   public void setLastModified(long lastModified)
   {
      this.lastModified = lastModified;
   }

   public Collection<String> getSupportedOptions()
   {
      return Collections.unmodifiableSet(supportedOptions);
   }

   /**
    * Public for tests
    *
    * @param option
    */
   public void setSupportedOption(String option)
   {
      if (WSRP2Constants.OPTIONS_COPYPORTLETS.equals(option) || WSRP2Constants.OPTIONS_EVENTS.equals(option)
         || WSRP2Constants.OPTIONS_EXPORT.equals(option) || WSRP2Constants.OPTIONS_IMPORT.equals(option)
         || WSRP2Constants.OPTIONS_LEASING.equals(option))
      {
         if (supportedOptions.isEmpty())
         {
            supportedOptions = new HashSet<String>(5);
         }
         supportedOptions.add(option);
      }
      else
      {
         throw new IllegalArgumentException("Invalid option: " + option);
      }
   }
}
TOP

Related Classes of org.gatein.wsrp.consumer.ProducerInfo

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.