/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.apache.jetspeed.tools.pamanager;
import java.io.File;
import java.io.IOException;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jetspeed.cluster.NodeManager;
import org.apache.jetspeed.components.portletentity.PortletEntityAccessComponent;
import org.apache.jetspeed.components.portletentity.PortletEntityNotDeletedException;
import org.apache.jetspeed.components.portletregistry.PortletRegistry;
import org.apache.jetspeed.components.portletregistry.RegistryException;
import org.apache.jetspeed.container.window.PortletWindowAccessor;
import org.apache.jetspeed.factory.PortletFactory;
import org.apache.jetspeed.om.common.portlet.MutablePortletApplication;
import org.apache.jetspeed.om.common.servlet.MutableWebApplication;
import org.apache.jetspeed.prefs.PreferencesProvider;
import org.apache.jetspeed.search.SearchEngine;
import org.apache.jetspeed.security.PermissionManager;
import org.apache.jetspeed.security.PortletPermission;
import org.apache.jetspeed.security.Role;
import org.apache.jetspeed.security.RoleManager;
import org.apache.jetspeed.security.SecurityException;
import org.apache.jetspeed.util.DirectoryHelper;
import org.apache.jetspeed.util.FileSystemHelper;
import org.apache.jetspeed.util.MultiFileChecksumHelper;
import org.apache.jetspeed.util.descriptor.PortletApplicationWar;
import org.apache.pluto.om.common.SecurityRole;
import org.apache.pluto.om.entity.PortletEntity;
import org.apache.pluto.om.entity.PortletEntityCtrl;
import org.apache.pluto.om.portlet.PortletDefinition;
/**
* PortletApplicationManager
*
* @author <a href="mailto:ate@douma.nu">Ate Douma</a>
* @version $Id: PortletApplicationManager.java,v 1.21 2005/04/09 00:24:44 shinsuke Exp $
*/
public class PortletApplicationManager implements PortletApplicationManagement
{
private static int DEFAULT_DESCRIPTOR_CHANGE_MONITOR_INTERVAL = 10*1000; // 10 seconds
private static int DEFAULT_MAX_RETRIED_STARTS = 10; // 10 times retry PA
private static final Log log = LogFactory.getLog("deployment");
protected PortletEntityAccessComponent entityAccess;
protected PortletFactory portletFactory;
protected PreferencesProvider prefs;
protected PortletRegistry registry;
protected PortletWindowAccessor windowAccess;
protected SearchEngine searchEngine;
protected RoleManager roleManager;
protected PermissionManager permissionManager;
protected boolean autoCreateRoles;
protected List permissionRoles;
protected int descriptorChangeMonitorInterval = DEFAULT_DESCRIPTOR_CHANGE_MONITOR_INTERVAL;
/**
* holds the max number of retries in case of unsuccessful PA start
* this addresses possible startup errors in clustered environments
*/
protected int maxRetriedStarts = DEFAULT_MAX_RETRIED_STARTS;
protected DescriptorChangeMonitor monitor = null;
protected boolean started;
protected String appRoot;
protected NodeManager nodeManager;
protected PortletApplicationManagement pamProxy;
protected boolean startOnSetPAMProxy;
/**
* Creates a new PortletApplicationManager object.
*/
public PortletApplicationManager(PortletFactory portletFactory, PreferencesProvider prefs, PortletRegistry registry,
PortletEntityAccessComponent entityAccess, PortletWindowAccessor windowAccess,
PermissionManager permissionManager, SearchEngine searchEngine,
RoleManager roleManager, List permissionRoles, NodeManager nodeManager, String appRoot)
{
this.portletFactory = portletFactory;
this.prefs = prefs;
this.registry = registry;
this.entityAccess = entityAccess;
this.windowAccess = windowAccess;
this.permissionManager = permissionManager;
this.searchEngine = searchEngine;
this.roleManager = roleManager;
this.permissionRoles = permissionRoles;
this.nodeManager = nodeManager;
this.appRoot = appRoot;
}
public void setPAMProxy(PortletApplicationManagement pamProxy)
{
this.pamProxy = pamProxy;
if (!started && startOnSetPAMProxy)
{
start();
}
}
public void start()
{
if ( pamProxy == null)
{
startOnSetPAMProxy = true;
return;
}
if ( descriptorChangeMonitorInterval > 0 )
{
try
{
monitor = new DescriptorChangeMonitor(Thread.currentThread().getThreadGroup(),
"PortletApplicationManager Descriptor Change Monitor Thread", pamProxy, descriptorChangeMonitorInterval, maxRetriedStarts);
monitor.setContextClassLoader(getClass().getClassLoader());
monitor.start();
log.info("PortletApplicationManager Descriptor Change Monitor started!");
}
catch (Exception e)
{
log.warn("Unable to start PortletApplicationManager Descriptor Change Monitor: "+ e.toString(), e);
monitor.safeStop();
monitor = null;
}
}
started = true;
}
public void stop()
{
started = false;
if (monitor != null)
{
monitor.safeStop();
monitor = null;
}
}
public boolean isStarted()
{
return started;
}
public void setRoleManager(RoleManager roleManager)
{
this.roleManager = roleManager;
}
public void setAutoCreateRoles(boolean autoCreateRoles)
{
this.autoCreateRoles = autoCreateRoles;
}
public void setSearchEngine(SearchEngine searchEngine)
{
this.searchEngine = searchEngine;
}
protected void checkStarted()
{
if (!started)
{
throw new IllegalStateException("Not started yet");
}
}
public void startLocalPortletApplication(String contextName, FileSystemHelper warStruct,
ClassLoader paClassLoader)
throws RegistryException
{
checkStarted();
retryStartPortletApplication(contextName, "/"+contextName, warStruct, paClassLoader, MutablePortletApplication.LOCAL);
}
public void startInternalApplication(String contextName) throws RegistryException
{
checkStarted();
File webinf = new File (appRoot);
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
DirectoryHelper dir = new DirectoryHelper(webinf);
String appName = (contextName.startsWith("/")) ? contextName.substring(1) : contextName;
MutablePortletApplication app = registry.getPortletApplicationByIdentifier(appName);
if (app != null && app.getApplicationType() == MutablePortletApplication.LOCAL)
{
app.setApplicationType(MutablePortletApplication.INTERNAL);
registry.updatePortletApplication(app);
}
retryStartPortletApplication(contextName, "/"+contextName, dir, contextClassLoader, MutablePortletApplication.INTERNAL);
// startInternal(contextName, warStruct, paClassLoader, true);
}
public void startPortletApplication(String contextName, FileSystemHelper warStruct,
ClassLoader paClassLoader)
throws RegistryException
{
startPortletApplication(contextName, "/"+contextName, warStruct, paClassLoader);
}
public void startPortletApplication(String contextName, String contextPath, FileSystemHelper warStruct,
ClassLoader paClassLoader) throws RegistryException
{
checkStarted();
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try
{
retryStartPortletApplication(contextName, contextPath, warStruct, paClassLoader, MutablePortletApplication.WEBAPP);
}
finally
{
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
protected void retryStartPortletApplication(String contextName, String contextPath, FileSystemHelper warStruct, ClassLoader paClassLoader, int paType) throws RegistryException
{
// Retry to start application according to configuration. Note
// that this method is not declared transactional to allow clean
// retries within a single transaction.
RegistryException tryStartException = null;
for (int i = 0; (i < maxRetriedStarts+1); i++)
{
try
{
// try to start portlet application
pamProxy.tryStartPortletApplication(contextName, contextPath, warStruct, paClassLoader, paType, 0, true);
// continue on success
tryStartException = null;
break;
}
catch (RegistryException re)
{
// save exception
tryStartException = re;
}
// brief pause between retries to let portlet application
// state settle
try
{
Thread.sleep(50);
}
catch (InterruptedException ie)
{
}
}
// throw try start exception
if (tryStartException != null)
{
log.error("Unable to start portlet application after "+maxRetriedStarts+" retries: "+tryStartException, tryStartException);
throw tryStartException;
}
}
public void tryStartPortletApplication(String contextName, String contextPath, FileSystemHelper warStruct, ClassLoader paClassLoader, int paType, long checksum, boolean silent) throws RegistryException
{
attemptStartPA(contextName, contextPath, warStruct, paClassLoader, paType, checksum, silent);
}
public void stopLocalPortletApplication(String contextName)
throws RegistryException
{
stopPA(contextName, MutablePortletApplication.LOCAL);
}
public void stopPortletApplication(String contextName)
throws RegistryException
{
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try
{
stopPA(contextName, MutablePortletApplication.WEBAPP);
}
finally
{
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
public void unregisterPortletApplication(String paName)
throws RegistryException
{
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try
{
MutablePortletApplication pa = null;
try
{
pa = registry.getPortletApplication(paName);
}
catch (Exception e)
{
// ignore errors during portal shutdown
}
if (pa != null)
{
if (portletFactory.isPortletApplicationRegistered(pa))
{
throw new RegistryException("Portlet Application " + paName + " still running");
}
unregisterPortletApplication(pa, true);
try
{
if (nodeManager != null)
nodeManager.removeNode(paName);
}
catch (Exception ee)
{
// we actually do not care about an exception in the remove operation...
}
}
}
finally
{
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
protected void checkValidContextName(String contextName, boolean local)
throws RegistryException
{
int prefixLength = LOCAL_PA_PREFIX.length();
if ((contextName.length() >= prefixLength)
&& contextName.substring(0, prefixLength).equalsIgnoreCase(LOCAL_PA_PREFIX))
{
if (!local)
{
throw new RegistryException("Prefix \"" + LOCAL_PA_PREFIX
+ "\" is reserved for Local Portlet Applications only.");
}
}
else if (local)
{
throw new RegistryException("Prefix \"" + LOCAL_PA_PREFIX
+ "\" is required for Local Portlet Applications.");
}
}
protected MutablePortletApplication registerPortletApplication(PortletApplicationWar paWar,
MutablePortletApplication oldPA, int paType, ClassLoader paClassLoader, boolean silent)
throws RegistryException
{
if (oldPA != null)
{
unregisterPortletApplication(oldPA, false);
oldPA = null;
}
MutablePortletApplication pa = null;
boolean registered = false;
String paName = paWar.getPortletApplicationName();
try
{
log.info("Loading portlet.xml...." + paName);
prefs.clearCachedApplicationPreferences(paName);
pa = paWar.createPortletApp(true, paClassLoader);
pa.setApplicationType(paType);
// load the web.xml
log.info("Loading web.xml...." + paName);
MutableWebApplication wa = paWar.createWebApp();
paWar.validate();
if (paType == MutablePortletApplication.LOCAL)
{
wa.setContextRoot("<portal>");
}
else if (paType == MutablePortletApplication.INTERNAL)
{
// TODO: this is screwing up the PSML as its set all over the place to "jetspeed-layouts", not good
wa.setContextRoot("/" + paName);
}
pa.setWebApplicationDefinition(wa);
// Make sure existing entities are refreshed with the most
// recent PortletDefintion.
Collection portletDefs = pa.getPortletDefinitions();
if(portletDefs != null && portletDefs.size() > 0)
{
Iterator pdItr = portletDefs.iterator();
while(pdItr.hasNext())
{
PortletDefinition pd = (PortletDefinition) pdItr.next();
Collection portletEntites = entityAccess.getPortletEntities(pd);
if(portletEntites != null && portletEntites.size() > 0)
{
Iterator peItr = portletEntites.iterator();
while(peItr.hasNext())
{
PortletEntityCtrl portletEntity = (PortletEntityCtrl) peItr.next();
portletEntity.setPortletDefinition(pd);
}
}
}
}
}
catch (Exception e)
{
// clear any cached preferences to be rolled back
prefs.clearCachedApplicationPreferences(paName);
String msg = "Failed to load portlet application for "
+ paWar.getPortletApplicationName();
if (!silent || log.isDebugEnabled())
{
log.error(msg, e);
}
throw new RegistryException(msg);
}
// register the portlet application
try
{
registry.registerPortletApplication(pa);
registered = true;
log.info("Registered the portlet application " + paName);
// add to search engine result
this.updateSearchEngine(false, pa);
// and add to the current node info
if (nodeManager != null)
{
nodeManager.addNode(new Long(pa.getId().toString()), pa.getName());
}
// grant default permissions to portlet application
grantDefaultPermissions(paName);
if ( autoCreateRoles && roleManager != null && pa.getWebApplicationDefinition().getSecurityRoles() != null )
{
try
{
Iterator rolesIter = pa.getWebApplicationDefinition().getSecurityRoles().iterator();
SecurityRole sr;
while ( rolesIter.hasNext() )
{
sr = (SecurityRole)rolesIter.next();
if ( !roleManager.roleExists(sr.getRoleName()) )
{
roleManager.addRole(sr.getRoleName());
log.info("AutoCreated role: "+sr.getRoleName()+" from portlet application "+paName+" its web definition");
}
}
}
catch (SecurityException sex)
{
log.warn("Failed to autoCreate roles for portlet application " + paName+": "+sex.getMessage(), sex);
}
}
return pa;
}
catch (Exception e)
{
// clear any cached preferences to be rolled back
prefs.clearCachedApplicationPreferences(paName);
String msg = "Failed to register portlet application, " + paName;
if (!silent || log.isDebugEnabled())
{
log.error(msg, e);
}
if (registered)
{
try
{
unregisterPortletApplication(pa, (paType == MutablePortletApplication.LOCAL));
}
catch (Exception re)
{
if (!silent || log.isDebugEnabled())
{
log.error("Failed to rollback registration of portlet application " + paName, re);
}
}
}
throw new RegistryException(msg, e);
}
}
protected void attemptStartPA(String contextName, String contextPath, FileSystemHelper warStruct,
ClassLoader paClassLoader, int paType, long checksum, boolean silent)
throws RegistryException
{
boolean register = true;
boolean monitored = false;
DescriptorChangeMonitor changeMonitor = this.monitor;
if (changeMonitor != null)
{
monitored = changeMonitor.isMonitored(contextName);
}
if (log.isDebugEnabled())
{
log.debug("Is portlet application " + contextName + " monitored? -> " + monitored);
}
PortletApplicationWar paWar = null;
try
{
if (log.isDebugEnabled())
{
log.debug("Try to start portlet application " + contextName + ".");
}
// create PA from war (file) structure
// paWar = new PortletApplicationWar(warStruct, contextName, "/" + contextName, checksum);
paWar = new PortletApplicationWar(warStruct, contextName, contextPath, checksum);
try
{
if (paClassLoader == null)
{
paClassLoader = paWar.createClassloader(this.getClass().getClassLoader());
}
// create checksum from PA descriptors
checksum = paWar.getPortletApplicationChecksum();
if (log.isDebugEnabled())
{
log.debug("New checksum for portlet application " + contextName + " is " + checksum);
}
}
catch (IOException e)
{
String msg = "Invalid PA WAR for " + contextName;
if (!silent || log.isDebugEnabled())
{
log.error(msg, e);
}
if ( paClassLoader == null )
{
// nothing to be done about it anymore: this pa is beyond repair :(
throw new RegistryException(e);
}
register = false;
}
// try to get the PA from database by context name
MutablePortletApplication pa = registry.getPortletApplication(contextName);
if (pa != null)
{
if (log.isDebugEnabled())
{
log.debug("Portlet Application " + contextName + " found in registry.");
}
if ( pa.getApplicationType() != paType )
{
throw new RegistryException("Cannot start portlet application "+contextName+": as Application Types don't match: " + pa.getApplicationType() + " != " + paType);
}
if (!monitored && changeMonitor != null)
{
changeMonitor.remove(contextName);
}
if (log.isDebugEnabled())
{
log.debug("unregistering portlet application " + contextName + "...");
}
portletFactory.unregisterPortletApplication(pa);
}
// if (register && (pa == null || checksum != pa.getChecksum()))
if (register)
{
if (pa == null)
{
// new
try
{
if (log.isDebugEnabled())
{
log.debug("Register new portlet application " + contextName + ".");
}
pa = registerPortletApplication(paWar, pa, paType, paClassLoader, silent);
}
catch (Exception e)
{
String msg = "Error register new portlet application " + contextName + ".";
if (log.isDebugEnabled())
{
log.debug(msg);
}
throw new RegistryException(msg);
}
}
else
{
if (log.isDebugEnabled())
{
log.debug("Re-register existing portlet application " + contextName + ".");
}
int status = nodeManager.checkNode(new Long(pa.getId().toString()), pa.getName());
boolean reregister = false;
boolean deploy = false;
switch (status)
{
case NodeManager.NODE_NEW:
{
if (log.isDebugEnabled())
{
log.debug("Node for Portlet application " + contextName + " is NEW.");
}
//only reason is that the file got somehow corrupted
// so we really do not know what is going on here...
// the best chance at this point is to reregister (which might be the absolute wrong choice)
log.warn("The portlet application " + pa.getName() + " is registered in the database but not locally .... we will reregister");
reregister = true;
if (checksum != pa.getChecksum())
{
log.warn("The provided portlet application " + pa.getName() + " is a different version than in the database (db-checksum=" + pa.getChecksum() + ", local-checksum=: " + checksum + ") .... we will redeploy (also to the database)");
deploy = true;
}
break;
}
case NodeManager.NODE_SAVED:
{
if (log.isDebugEnabled())
{
log.debug("Node for Portlet application " + contextName + " is SAVED.");
}
if (checksum != pa.getChecksum())
{
log.warn("The provided portlet application " + pa.getName() + " is a different version than in the local node info and the database (db-checksum=" + pa.getChecksum() + ", local-checksum=: " + checksum + ") .... we will reregister AND redeploy (also to the database)");
//database and local node info are in synch, so we assume that this is a brand new
// war .... let's deploy
reregister = true;
deploy = true;
}
break;
}
case NodeManager.NODE_OUTDATED:
{
// new version in database, maybe changed by a different cluster node
if (log.isDebugEnabled())
{
log.debug("Node for Portlet application " + contextName + " is OUTDATED (local PA.id < DB PA.id).");
}
//database version is older (determined by id) than the database
//let's deploy and reregister
if (checksum != pa.getChecksum())
{
log.error("The portlet application " + pa.getName() + " provided for the upgrade IS WRONG. The database checksum= " + pa.getChecksum() + ", but the local=" + checksum + "....THIS NEEDS TO BE CORRECTED");
// if the checksums do not match make sure the database is updated with the new PA from file system
// I've observed "unavailable PA" in clustered env for the cluster node that reported OUTDATED state
deploy = true;
}
reregister = true;
break;
}
}
if (deploy)
{
if (log.isDebugEnabled())
{
log.debug("Register (deploy=true) Portlet application " + contextName + " in database.");
}
pa = registerPortletApplication(paWar, pa, paType, paClassLoader, silent);
}
else
if (reregister)
{
if (log.isDebugEnabled())
{
log.debug("Re-Register (reregister=true) Portlet application " + contextName + ".");
}
// add to search engine result
this.updateSearchEngine(true, pa);
this.updateSearchEngine(false, pa);
// and add to the current node info
try
{
nodeManager.addNode(new Long(pa.getId().toString()), pa.getName());
} catch (Exception e)
{
log.error("Adding node for portlet application " + pa.getName() + " caused exception" , e);
}
}
}
}
if (register)
{
if (log.isDebugEnabled())
{
log.debug("Register Portlet application " + contextName + " in portlet factory.");
}
portletFactory.registerPortletApplication(pa, paClassLoader);
}
if (!monitored && changeMonitor != null)
{
if (log.isDebugEnabled())
{
log.debug("Add change monitor for application " + contextName + " with checksum " + checksum + ".");
}
changeMonitor.monitor(contextName, contextPath, paClassLoader, paType, warStruct.getRootDirectory(), checksum);
}
}
catch (Exception e)
{
String msg = "Error starting portlet application " + contextName;
if (!silent || log.isDebugEnabled())
{
log.error(msg, e);
}
// monitor PA for changes
// do not add monitor if a monitor already exists
if (!monitored && changeMonitor != null)
{
// this code should be hit only during startup process
if (log.isDebugEnabled())
{
log.debug("Add change monitor for application " + contextName + " and set unsuccessful starts to 1.");
}
changeMonitor.monitor(contextName, contextPath, paClassLoader, paType, warStruct.getRootDirectory(), checksum);
changeMonitor.get(contextName).setUnsuccessfulStarts(1);
}
throw new RegistryException(msg);
}
finally
{
if (paWar != null)
{
try
{
paWar.close();
}
catch (IOException e)
{
log.error("Failed to close PA WAR for " + contextName, e);
}
}
}
}
protected void stopPA(String contextName, int paType)
throws RegistryException
{
MutablePortletApplication pa = null;
try
{
pa = registry.getPortletApplication(contextName);
}
catch (Exception e)
{
// ignore errors during portal shutdown
}
if (pa != null && pa.getApplicationType() != paType)
{
throw new RegistryException("Cannot stop portlet application "+contextName+": as Application Types don't match: " + pa.getApplicationType() + " != " + paType);
}
DescriptorChangeMonitor monitor = this.monitor;
if ( monitor != null )
{
monitor.remove(contextName);
}
if (pa != null)
{
portletFactory.unregisterPortletApplication(pa);
}
}
protected void updateSearchEngine(boolean remove,MutablePortletApplication pa )
{
if (searchEngine != null)
{
if (remove)
{
searchEngine.remove(pa);
searchEngine.remove(pa.getPortletDefinitions());
log.info("Un-Registered the portlet application in the search engine... " + pa.getName());
}
else
{
searchEngine.add(pa);
searchEngine.add(pa.getPortletDefinitions());
log.info("Registered the portlet application in the search engine... " + pa.getName());
}
}
}
protected void unregisterPortletApplication(MutablePortletApplication pa,
boolean purgeEntityInfo)
throws RegistryException
{
updateSearchEngine(true,pa);
log.info("Remove all registry entries defined for portlet application " + pa.getName());
Iterator portlets = pa.getPortletDefinitions().iterator();
while (portlets.hasNext())
{
PortletDefinition portletDefinition = (PortletDefinition) portlets.next();
Iterator entities = entityAccess.getPortletEntities(portletDefinition)
.iterator();
while (entities.hasNext())
{
PortletEntity entity = (PortletEntity) entities.next();
if (purgeEntityInfo)
{
try
{
entityAccess.removePortletEntity(entity);
}
catch (PortletEntityNotDeletedException e)
{
String msg = "Failed to delete Portlet Entity " + entity.getId();
log.error(msg, e);
throw new RegistryException(msg, e);
}
}
entityAccess.removeFromCache(entity);
windowAccess.removeWindows(entity);
}
}
// todo keep (User)Prefs?
registry.removeApplication(pa);
revokeDefaultPermissions(pa.getName());
}
protected void grantDefaultPermissions(String paName)
{
try
{
// create a default permission for this portlet app, granting configured roles to the portlet application
Iterator roles = permissionRoles.iterator();
while (roles.hasNext())
{
String roleName = (String)roles.next();
Role userRole = roleManager.getRole(roleName);
if (userRole != null)
{
Permission permission = new PortletPermission(paName + "::*", "view, edit");
if (!permissionManager.permissionExists(permission))
{
permissionManager.addPermission(permission);
permissionManager.grantPermission(userRole.getPrincipal(), permission);
}
}
}
}
catch (SecurityException e)
{
log.error("Error granting default permissions for " + paName, e);
}
}
protected void revokeDefaultPermissions(String paName)
{
try
{
Iterator roles = permissionRoles.iterator();
while (roles.hasNext())
{
String roleName = (String)roles.next();
Role userRole = roleManager.getRole(roleName);
if (userRole != null)
{
Permission permission = new PortletPermission(paName + "::*", "view, edit");
if (permissionManager.permissionExists(permission))
{
permissionManager.removePermission(permission);
}
}
}
}
catch (SecurityException e)
{
log.error("Error revoking default permissions for " + paName, e);
}
}
public int getDescriptorChangeMonitorInterval()
{
return descriptorChangeMonitorInterval/1000;
}
public void setDescriptorChangeMonitorInterval(int descriptorChangeMonitorInterval)
{
this.descriptorChangeMonitorInterval = descriptorChangeMonitorInterval*1000;
}
private static class DescriptorChangeMonitor extends Thread
{
private static class DescriptorChangeMonitorInfo
{
private String contextName;
private String contextPath;
private ClassLoader paClassLoader;
private int paType;
private File paDir;
private File[] descriptors;
private long descriptorModificationTime;
private long extendedDescriptorModificationTime;
private long checksum;
private boolean obsolete;
/**
* holds the number of unsuccessful starts of the monitored PA
*/
private int unsuccessfulStarts;
/*
* Constructor only used for looking up the matching registered one in monitorsInfo
*/
public DescriptorChangeMonitorInfo(String contextName)
{
this.contextName = contextName;
}
public DescriptorChangeMonitorInfo(String contextName, String contextPath, ClassLoader paClassLoader, int paType, File paDir, long checksum)
{
this.contextName = contextName;
this.contextPath = contextPath;
this.paClassLoader = paClassLoader;
this.paType = paType;
this.paDir = paDir.isAbsolute() ? paDir : paDir.getAbsoluteFile();
this.checksum = checksum;
this.descriptors = new File[] {
new File(paDir, PortletApplicationWar.WEB_XML_PATH),
new File(paDir, PortletApplicationWar.PORTLET_XML_PATH),
new File(paDir, PortletApplicationWar.EXTENDED_PORTLET_XML_PATH) };
descriptorModificationTime = descriptors[1].lastModified();
extendedDescriptorModificationTime = descriptors[2].lastModified();
}
public String getContextName()
{
return contextName;
}
public ClassLoader getPAClassLoader()
{
return paClassLoader;
}
public int getPortletApplicationType()
{
return paType;
}
public File getPADir()
{
return paDir;
}
public long getChecksum()
{
return checksum;
}
public boolean isChanged()
{
if ( !obsolete)
{
long newDescriptorModificationTime = descriptors[1].lastModified();
long newExtendedDescriptorModificationTime = descriptors[2].lastModified();
if ( descriptorModificationTime != newDescriptorModificationTime ||
extendedDescriptorModificationTime != newExtendedDescriptorModificationTime )
{
descriptorModificationTime = newDescriptorModificationTime;
extendedDescriptorModificationTime = newExtendedDescriptorModificationTime;
long newChecksum = MultiFileChecksumHelper.getChecksum(descriptors);
if (log.isDebugEnabled())
{
log.debug("checksum check for descriptors for application " + contextName + ": old (" + checksum + ") new (" + newChecksum + ").");
}
if ( checksum != newChecksum )
{
if (log.isDebugEnabled())
{
log.debug("portlet descriptors for application " + contextName + " have changed.");
}
checksum = newChecksum;
// reset this to restart unsuccessful PA start handling for evers PA descriptor change
unsuccessfulStarts = 0;
return true;
}
}
}
return false;
}
public void setObsolete()
{
obsolete = true;
}
public boolean isObsolete()
{
return obsolete;
}
public int getUnsuccessfulStarts()
{
return unsuccessfulStarts;
}
public void setUnsuccessfulStarts(int unsuccessfulStarts)
{
this.unsuccessfulStarts = unsuccessfulStarts;
}
public String getContextPath()
{
return contextPath;
}
}
private PortletApplicationManagement pam;
private long interval;
private boolean started = true;
private ArrayList monitorInfos;
private int maxRetriedStarts;
public DescriptorChangeMonitor(ThreadGroup group, String name, PortletApplicationManagement pam, long interval, int maxretriedStarts)
{
super(group, name);
this.pam = pam;
this.interval = interval;
monitorInfos = new ArrayList();
setPriority(MIN_PRIORITY);
setDaemon(true);
this.maxRetriedStarts = maxretriedStarts;
}
public void run()
{
try
{
sleep(interval);
}
catch (InterruptedException e)
{
}
while (started)
{
checkDescriptorChanges();
try
{
sleep(interval);
}
catch (InterruptedException e)
{
}
}
}
/**
* notifies a switch variable that exits the watcher's montior loop started in the <code>run()</code> method.
*/
public synchronized void safeStop()
{
started = false;
monitorInfos.clear();
}
public synchronized void monitor(String contextName, String contextPath, ClassLoader paClassLoader, int paType, File paDir, long checksum)
{
monitorInfos.add(new DescriptorChangeMonitorInfo(contextName, contextPath, paClassLoader, paType, paDir, checksum));
}
public synchronized void remove(String contextName)
{
DescriptorChangeMonitorInfo monitorInfo;
for ( int i = monitorInfos.size()-1; i > -1; i-- )
{
monitorInfo = (DescriptorChangeMonitorInfo)monitorInfos.get(i);
if (contextName.equals(monitorInfo.getContextName()))
{
// will be removed by checkDescriptorChanges on next iteration
monitorInfo.setObsolete();
break;
}
}
}
public synchronized DescriptorChangeMonitorInfo get(String contextName)
{
DescriptorChangeMonitorInfo monitorInfo;
for ( int i = monitorInfos.size()-1; i > -1; i-- )
{
monitorInfo = (DescriptorChangeMonitorInfo)monitorInfos.get(i);
if (contextName.equals(monitorInfo.getContextName()))
{
return monitorInfo;
}
}
return null;
}
public boolean isMonitored(String contextName)
{
DescriptorChangeMonitorInfo monitorInfo = this.get(contextName);
if (monitorInfo != null && !monitorInfo.isObsolete())
{
return true;
}
return false;
}
private void checkDescriptorChanges()
{
int size;
synchronized (this)
{
size = monitorInfos.size();
}
if (log.isDebugEnabled())
{
log.debug("check for portlet application descriptor changes.");
}
for (int i = size-1; i > -1; i--)
{
DescriptorChangeMonitorInfo monitorInfo;
synchronized (this)
{
if ( started )
{
monitorInfo = (DescriptorChangeMonitorInfo)monitorInfos.get(i);
if (monitorInfo.isObsolete())
{
monitorInfos.remove(i);
}
else
{
try
{
int unsuccessfulStarts = monitorInfo.getUnsuccessfulStarts();
// try to restart PA if the PA-descriptors have change
// OR (if we encountered an exception while starting the PA)
// keep on trying to restart PA until maxRetriedStarts is reached
// This ensures we finally startup in a clustered enviroment, where parallel registration
// of PAs could lead to contraint violation eceptions in DB.
// see https://issues.apache.org/jira/browse/JS2-666
// Note: monitorInfo.isChanged() will reset unsuccessfulStarts to 0 if a PA descriptor change
// has been detected (monitorInfo.isChanged() == true).
if (monitorInfo.isChanged() || (unsuccessfulStarts > 0 && unsuccessfulStarts <= maxRetriedStarts))
{
try
{
pam.tryStartPortletApplication(monitorInfo.getContextName(), monitorInfo.getContextPath(), new DirectoryHelper(monitorInfo.getPADir()),
monitorInfo.getPAClassLoader(), monitorInfo.getPortletApplicationType(), monitorInfo.getChecksum(), true);
// great! we have a successful start. set unsuccessful starts to 0
monitorInfo.setUnsuccessfulStarts(0);
}
catch (Exception e)
{
if (monitorInfo.isChanged())
{
log.error("Failed to restart PortletApplication "+monitorInfo.getContextName(),e);
}
else if (log.isWarnEnabled())
{
log.warn("Failed to restart PortletApplication "+monitorInfo.getContextName(),e);
}
// we encountered an error while starting the PA
// this could result from clustered environments problems (see above)
// increase unsuccessfulStarts until the maxRetriedStarts is reached
monitorInfo.setUnsuccessfulStarts(unsuccessfulStarts + 1);
if (log.isDebugEnabled())
{
log.debug("Number of unsuccessful PA starts is " + monitorInfo.getUnsuccessfulStarts() + ".");
}
if (monitorInfo.getUnsuccessfulStarts() > maxRetriedStarts)
{
log.error("Max number of retries (" + maxRetriedStarts +") reached. Ignoring Monitor for " + monitorInfo.getContextName());
}
}
}
}
catch (Exception e)
{
// ignore filesystem and/or descriptor errors, maybe next time round they'll be fixed again
log.error("Descriptor Change check failure for PortletApplication "+monitorInfo.getContextName(),e);
}
}
}
}
}
}
}
public void setMaxRetriedStarts(int maxRetriedStarts)
{
this.maxRetriedStarts = maxRetriedStarts;
}
public int getMaxRetriedStarts()
{
return maxRetriedStarts;
}
}