/*
* 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.integration.wsrp.structure;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.exoplatform.portal.config.DataStorage;
import org.exoplatform.portal.mop.Described;
import org.exoplatform.portal.mop.EventType;
import org.exoplatform.portal.mop.page.PageKey;
import org.exoplatform.portal.pom.spi.wsrp.WSRP;
import org.exoplatform.services.listener.Event;
import org.exoplatform.services.listener.Listener;
import org.gatein.common.util.ParameterValidation;
import org.gatein.mop.api.content.Customization;
import org.gatein.mop.api.workspace.ObjectType;
import org.gatein.mop.api.workspace.Page;
import org.gatein.mop.api.workspace.ui.UIComponent;
import org.gatein.mop.api.workspace.ui.UIContainer;
import org.gatein.mop.api.workspace.ui.UIWindow;
import org.gatein.pc.api.PortletContext;
import org.gatein.pc.api.PortletStateType;
import org.gatein.pc.api.StatefulPortletContext;
import org.gatein.wsrp.api.context.ConsumerStructureProvider;
/**
* @author <a href="mailto:chris.laprun@jboss.com">Chris Laprun</a>
* @version $Revision$
*/
public class MOPConsumerStructureProvider extends Listener implements ConsumerStructureProvider {
private final PortalStructureAccess structureAccess;
private Map<String, PageInfo> pageInfos;
private boolean pagesHaveBeenInitialized;
public MOPConsumerStructureProvider(PortalStructureAccess structureAccess) {
ParameterValidation.throwIllegalArgExceptionIfNull(structureAccess, "PortalStructureAccess");
this.structureAccess = structureAccess;
pageInfos = new HashMap<String, PageInfo>();
}
public List<String> getPageIdentifiers() {
if (!pagesHaveBeenInitialized) {
// initialize page information
Collection<Page> pages = structureAccess.getPages();
for (Page page : pages) {
addPage(page);
}
pagesHaveBeenInitialized = true;
}
LinkedList<String> identifiers = new LinkedList<String>(pageInfos.keySet());
Collections.sort(identifiers);
return identifiers;
}
private void addPage(Page page) {
PageInfo pageInfo = new PageInfo(page);
pageInfos.put(pageInfo.getId(), pageInfo);
UIContainer container = page.getRootComponent();
processContainer(container, pageInfo);
Collection<Page> children = page.getChildren();
if (ParameterValidation.existsAndIsNotEmpty(children)) {
for (Page child : children) {
addPage(child);
}
}
}
public List<String> getWindowIdentifiersFor(String pageId) {
PageInfo pageInfo = pageInfos.get(pageId);
if (pageInfo == null) {
throw new IllegalArgumentException("Page '" + pageId + "' does not exist.");
}
return pageInfo.getChildrenWindows();
}
private void processContainer(UIContainer container, PageInfo pageInfo) {
if (container != null) {
List<UIComponent> components = container.getComponents();
for (UIComponent component : components) {
ObjectType<? extends UIComponent> type = component.getObjectType();
if (ObjectType.WINDOW.equals(type)) {
Described described = component.adapt(Described.class);
String name = described.getName();
if (name == null) {
name = described.getDescription();
}
pageInfo.addWindow(name, component.getObjectId());
} else if (ObjectType.CONTAINER.equals(type)) {
processContainer((UIContainer) component, pageInfo);
} else {
// ignore
}
}
}
}
public void assignPortletToWindow(PortletContext portletContext, String windowId, String pageId,
String exportedPortletHandle) {
PageInfo pageInfo = pageInfos.get(pageId);
String uuid = pageInfo.getWindowUUID(windowId);
ParameterValidation.throwIllegalArgExceptionIfNull(uuid, "UUID for " + windowId);
// get the window
UIWindow window = structureAccess.getWindowFrom(uuid);
// construct the new customization state
WSRP wsrp = new WSRP();
String portletId = portletContext.getId();
wsrp.setPortletId(portletId);
if (portletContext instanceof StatefulPortletContext) {
StatefulPortletContext context = (StatefulPortletContext) portletContext;
if (PortletStateType.OPAQUE.equals(context.getType())) {
wsrp.setState((byte[]) context.getState());
} else {
throw new IllegalArgumentException("Don't know how to deal with state: " + context.getState());
}
}
// destroy existing customization as otherwise re-customizing will fail
Customization<?> customization = window.getCustomization();
customization.destroy();
// and re-customize
window.customize(WSRP.CONTENT_TYPE, portletId, wsrp);
// Change the window's name so that it's less confusing to users
Described described = window.adapt(Described.class);
String newName = exportedPortletHandle + " (remote)";
described.setName(newName); // should be the same as ApplicationRegistryService.REMOTE_DISPLAY_NAME_SUFFIX
// update window mappings
pageInfo.updateWindowName(windowId, newName);
structureAccess.saveChangesTo(window);
}
@Override
public void onEvent(Event event) throws Exception {
String eventName = event.getEventName();
// get the MOP page from the event data
final Object eventData = event.getData();
final Page page;
PageKey pageKey = null;
if (eventData instanceof PageKey) {
pageKey = (PageKey) eventData;
page = structureAccess.getPageFrom(pageKey);
} else {
org.exoplatform.portal.config.model.Page portalPage = (org.exoplatform.portal.config.model.Page) eventData;
page = structureAccess.getPageFrom(portalPage);
}
if (pageKey != null && page == null && EventType.PAGE_DESTROYED.equals(eventName)) {
// if we try to remove a page, when we get this event, the page has already been removed from JCR
// so we need to work around that fact by retrieving the corresponding PageInfo from the portal page title
// which should match the Described name and check that it matches the internal name before removing it
removePageByInternalName(pageKey.getName());
return;
}
if (page != null) {
if (EventType.PAGE_CREATED.equals(eventName)) {
// add information for new page
addPage(page);
} else if (EventType.PAGE_UPDATED.equals(eventName) || DataStorage.PAGE_UPDATED.equals(eventName)) {
removePage(page);
addPage(page);
}
}
}
private void removePage(Page page) {
final String id = PageInfo.getPageIdFor(page);
PageInfo pageInfo = pageInfos.get(id);
if (pageInfo != null && page.getName().equals(pageInfo.getInternalName())) {
// remove page info
pageInfos.remove(id);
}
}
private void removePageByInternalName(String internalName) {
Iterator<PageInfo> itr = pageInfos.values().iterator();
while (itr.hasNext()) {
if (itr.next().getInternalName().equals(internalName)) {
itr.remove();
return;
}
}
}
static class PageInfo {
private final String uuid;
private final Map<String, String> childrenWindows = new HashMap<String, String>();
/** Name as provided by Described */
private final String name;
/** Human readable identifier across sites as pages with the same name exist in different sites */
private final String id;
/** Name as automatically generated */
private final String internalName;
private PageInfo(Page page) {
this.uuid = page.getObjectId();
this.internalName = page.getName();
Described described = page.adapt(Described.class);
this.name = described.getName();
this.id = getPageIdFor(page);
}
static String getPageIdFor(Page page) {
Described described = page.adapt(Described.class);
return getPageIdFor(page.getSite().getName(), described.getName());
}
static String getPageIdFor(String siteName, String pageName) {
return siteName + " :: " + pageName;
}
public String getUUID() {
return uuid;
}
public String getInternalName() {
return internalName;
}
public List<String> getChildrenWindows() {
return new ArrayList<String>(childrenWindows.keySet());
}
public void addWindow(String windowName, String uuid) {
// if we don't have a window name, use the UUID
if (ParameterValidation.isNullOrEmpty(windowName)) {
windowName = uuid;
}
// add suffix in case we have several windows with the same name in the page
if (childrenWindows.containsKey(windowName)) {
if (windowName.endsWith("|")) {
windowName += "|";
} else {
windowName += windowName + " |";
}
}
childrenWindows.put(windowName, uuid);
}
public void updateWindowName(String oldWindowName, String newWindowName) {
String windowUUID = getWindowUUID(oldWindowName);
childrenWindows.remove(oldWindowName);
childrenWindows.put(newWindowName, windowUUID);
}
public String getName() {
return name;
}
public String getWindowUUID(String windowId) {
return childrenWindows.get(windowId);
}
public String getId() {
return id;
}
}
}