0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.jetspeed.tools.pamanager;
0018:
0019: import java.io.File;
0020: import java.io.IOException;
0021: import java.security.Permission;
0022: import java.util.ArrayList;
0023: import java.util.Collection;
0024: import java.util.Iterator;
0025: import java.util.List;
0026:
0027: import org.apache.commons.logging.Log;
0028: import org.apache.commons.logging.LogFactory;
0029: import org.apache.jetspeed.cluster.NodeManager;
0030: import org.apache.jetspeed.components.portletentity.PortletEntityAccessComponent;
0031: import org.apache.jetspeed.components.portletentity.PortletEntityNotDeletedException;
0032: import org.apache.jetspeed.components.portletregistry.PortletRegistry;
0033: import org.apache.jetspeed.components.portletregistry.RegistryException;
0034: import org.apache.jetspeed.container.window.PortletWindowAccessor;
0035: import org.apache.jetspeed.factory.PortletFactory;
0036: import org.apache.jetspeed.om.common.portlet.MutablePortletApplication;
0037: import org.apache.jetspeed.om.common.servlet.MutableWebApplication;
0038: import org.apache.jetspeed.search.SearchEngine;
0039: import org.apache.jetspeed.security.PermissionManager;
0040: import org.apache.jetspeed.security.PortletPermission;
0041: import org.apache.jetspeed.security.Role;
0042: import org.apache.jetspeed.security.RoleManager;
0043: import org.apache.jetspeed.security.SecurityException;
0044: import org.apache.jetspeed.util.DirectoryHelper;
0045: import org.apache.jetspeed.util.FileSystemHelper;
0046: import org.apache.jetspeed.util.MultiFileChecksumHelper;
0047: import org.apache.jetspeed.util.descriptor.PortletApplicationWar;
0048: import org.apache.pluto.om.common.SecurityRole;
0049: import org.apache.pluto.om.entity.PortletEntity;
0050: import org.apache.pluto.om.entity.PortletEntityCtrl;
0051: import org.apache.pluto.om.portlet.PortletDefinition;
0052:
0053: /**
0054: * PortletApplicationManager
0055: *
0056: * @author <a href="mailto:ate@douma.nu">Ate Douma</a>
0057: * @version $Id: PortletApplicationManager.java,v 1.21 2005/04/09 00:24:44 shinsuke Exp $
0058: */
0059: public class PortletApplicationManager implements
0060: PortletApplicationManagement {
0061: private static int DEFAULT_DESCRIPTOR_CHANGE_MONITOR_INTERVAL = 10 * 1000; // 10 seconds
0062: private static int DEFAULT_MAX_RETRIED_STARTS = 10; // 10 times retry PA
0063: private static final Log log = LogFactory.getLog("deployment");
0064:
0065: protected PortletEntityAccessComponent entityAccess;
0066: protected PortletFactory portletFactory;
0067: protected PortletRegistry registry;
0068: protected PortletWindowAccessor windowAccess;
0069: protected SearchEngine searchEngine;
0070: protected RoleManager roleManager;
0071: protected PermissionManager permissionManager;
0072: protected boolean autoCreateRoles;
0073: protected List permissionRoles;
0074: protected int descriptorChangeMonitorInterval = DEFAULT_DESCRIPTOR_CHANGE_MONITOR_INTERVAL;
0075: /**
0076: * holds the max number of retries in case of unsuccessful PA start
0077: * this addresses possible startup errors in clustered environments
0078: */
0079: protected int maxRetriedStarts = DEFAULT_MAX_RETRIED_STARTS;
0080: protected DescriptorChangeMonitor monitor;
0081: protected boolean started;
0082: protected String appRoot;
0083: protected NodeManager nodeManager;
0084:
0085: /**
0086: * Creates a new PortletApplicationManager object.
0087: */
0088: public PortletApplicationManager(PortletFactory portletFactory,
0089: PortletRegistry registry,
0090: PortletEntityAccessComponent entityAccess,
0091: PortletWindowAccessor windowAccess,
0092: PermissionManager permissionManager,
0093: SearchEngine searchEngine, RoleManager roleManager,
0094: List permissionRoles, NodeManager nodeManager,
0095: String appRoot) {
0096: this .portletFactory = portletFactory;
0097: this .registry = registry;
0098: this .entityAccess = entityAccess;
0099: this .windowAccess = windowAccess;
0100: this .permissionManager = permissionManager;
0101: this .searchEngine = searchEngine;
0102: this .roleManager = roleManager;
0103: this .permissionRoles = permissionRoles;
0104: this .nodeManager = nodeManager;
0105: this .appRoot = appRoot;
0106: }
0107:
0108: public void start() {
0109: if (descriptorChangeMonitorInterval > 0) {
0110: try {
0111: monitor = new DescriptorChangeMonitor(
0112: Thread.currentThread().getThreadGroup(),
0113: "PortletApplicationManager Descriptor Change Monitor Thread",
0114: this , descriptorChangeMonitorInterval,
0115: maxRetriedStarts);
0116:
0117: monitor.setContextClassLoader(getClass()
0118: .getClassLoader());
0119: monitor.start();
0120: log
0121: .info("PortletApplicationManager Descriptor Change Monitor started!");
0122: } catch (Exception e) {
0123: log.warn(
0124: "Unable to start PortletApplicationManager Descriptor Change Monitor: "
0125: + e.toString(), e);
0126: monitor.safeStop();
0127: monitor = null;
0128: }
0129: }
0130: started = true;
0131: }
0132:
0133: public void stop() {
0134: started = false;
0135: if (monitor != null) {
0136: monitor.safeStop();
0137: monitor = null;
0138: }
0139: }
0140:
0141: public boolean isStarted() {
0142: return started;
0143: }
0144:
0145: public void setRoleManager(RoleManager roleManager) {
0146: this .roleManager = roleManager;
0147: }
0148:
0149: public void setAutoCreateRoles(boolean autoCreateRoles) {
0150: this .autoCreateRoles = autoCreateRoles;
0151: }
0152:
0153: public void setSearchEngine(SearchEngine searchEngine) {
0154: this .searchEngine = searchEngine;
0155: }
0156:
0157: private void checkStarted() {
0158: if (!started) {
0159: throw new IllegalStateException("Not started yet");
0160: }
0161: }
0162:
0163: public void startLocalPortletApplication(String contextName,
0164: FileSystemHelper warStruct, ClassLoader paClassLoader)
0165: throws RegistryException {
0166: checkStarted();
0167: startPA(contextName, "/" + contextName, warStruct,
0168: paClassLoader, MutablePortletApplication.LOCAL);
0169: }
0170:
0171: public void startInternalApplication(String contextName)
0172: throws RegistryException {
0173: checkStarted();
0174: File webinf = new File(appRoot);
0175: ClassLoader contextClassLoader = Thread.currentThread()
0176: .getContextClassLoader();
0177: DirectoryHelper dir = new DirectoryHelper(webinf);
0178: String appName = (contextName.startsWith("/")) ? contextName
0179: .substring(1) : contextName;
0180: MutablePortletApplication app = registry
0181: .getPortletApplicationByIdentifier(appName);
0182: if (app != null
0183: && app.getApplicationType() == MutablePortletApplication.LOCAL) {
0184: app.setApplicationType(MutablePortletApplication.INTERNAL);
0185: registry.updatePortletApplication(app);
0186: }
0187: startPA(contextName, "/" + contextName, dir,
0188: contextClassLoader, MutablePortletApplication.INTERNAL);
0189: // startInternal(contextName, warStruct, paClassLoader, true);
0190: }
0191:
0192: public void startPortletApplication(String contextName,
0193: FileSystemHelper warStruct, ClassLoader paClassLoader)
0194: throws RegistryException {
0195: startPortletApplication(contextName, "/" + contextName,
0196: warStruct, paClassLoader);
0197: }
0198:
0199: public void startPortletApplication(String contextName,
0200: String contextPath, FileSystemHelper warStruct,
0201: ClassLoader paClassLoader) throws RegistryException {
0202: checkStarted();
0203: ClassLoader contextClassLoader = Thread.currentThread()
0204: .getContextClassLoader();
0205: Thread.currentThread().setContextClassLoader(
0206: this .getClass().getClassLoader());
0207: try {
0208: startPA(contextName, contextPath, warStruct, paClassLoader,
0209: MutablePortletApplication.WEBAPP);
0210: } finally {
0211: Thread.currentThread().setContextClassLoader(
0212: contextClassLoader);
0213: }
0214:
0215: }
0216:
0217: public void stopLocalPortletApplication(String contextName)
0218: throws RegistryException {
0219: stopPA(contextName, MutablePortletApplication.LOCAL);
0220: }
0221:
0222: public void stopPortletApplication(String contextName)
0223: throws RegistryException {
0224: ClassLoader contextClassLoader = Thread.currentThread()
0225: .getContextClassLoader();
0226: Thread.currentThread().setContextClassLoader(
0227: this .getClass().getClassLoader());
0228: try {
0229: stopPA(contextName, MutablePortletApplication.WEBAPP);
0230: } finally {
0231: Thread.currentThread().setContextClassLoader(
0232: contextClassLoader);
0233: }
0234: }
0235:
0236: public void unregisterPortletApplication(String paName)
0237: throws RegistryException {
0238: ClassLoader contextClassLoader = Thread.currentThread()
0239: .getContextClassLoader();
0240: Thread.currentThread().setContextClassLoader(
0241: this .getClass().getClassLoader());
0242: try {
0243: MutablePortletApplication pa = null;
0244:
0245: try {
0246: pa = registry.getPortletApplication(paName);
0247: } catch (Exception e) {
0248: // ignore errors during portal shutdown
0249: }
0250:
0251: if (pa != null) {
0252: if (portletFactory.isPortletApplicationRegistered(pa)) {
0253: throw new RegistryException("Portlet Application "
0254: + paName + " still running");
0255: }
0256:
0257: unregisterPortletApplication(pa, true);
0258: try {
0259: nodeManager.removeNode(paName);
0260: } catch (Exception ee) {
0261: // we actually do not care about an exception in the remove operation...
0262: }
0263: }
0264: } finally {
0265: Thread.currentThread().setContextClassLoader(
0266: contextClassLoader);
0267: }
0268: }
0269:
0270: protected void checkValidContextName(String contextName,
0271: boolean local) throws RegistryException {
0272: int prefixLength = LOCAL_PA_PREFIX.length();
0273:
0274: if ((contextName.length() >= prefixLength)
0275: && contextName.substring(0, prefixLength)
0276: .equalsIgnoreCase(LOCAL_PA_PREFIX)) {
0277: if (!local) {
0278: throw new RegistryException(
0279: "Prefix \""
0280: + LOCAL_PA_PREFIX
0281: + "\" is reserved for Local Portlet Applications only.");
0282: }
0283: } else if (local) {
0284: throw new RegistryException("Prefix \"" + LOCAL_PA_PREFIX
0285: + "\" is required for Local Portlet Applications.");
0286: }
0287: }
0288:
0289: protected MutablePortletApplication registerPortletApplication(
0290: PortletApplicationWar paWar,
0291: MutablePortletApplication oldPA, int paType,
0292: ClassLoader paClassLoader) throws RegistryException {
0293: if (oldPA != null) {
0294: unregisterPortletApplication(oldPA, false);
0295: oldPA = null;
0296: }
0297:
0298: MutablePortletApplication pa = null;
0299: boolean registered = false;
0300: String paName = paWar.getPortletApplicationName();
0301:
0302: try {
0303: log.info("Loading portlet.xml...." + paName);
0304: pa = paWar.createPortletApp(paClassLoader);
0305: pa.setApplicationType(paType);
0306:
0307: // load the web.xml
0308: log.info("Loading web.xml...." + paName);
0309: MutableWebApplication wa = paWar.createWebApp();
0310: paWar.validate();
0311:
0312: if (paType == MutablePortletApplication.LOCAL) {
0313: wa.setContextRoot("<portal>");
0314: } else if (paType == MutablePortletApplication.INTERNAL) {
0315: // TODO: this is screwing up the PSML as its set all over the place to "jetspeed-layouts", not good
0316: wa.setContextRoot("/" + paName);
0317: }
0318:
0319: pa.setWebApplicationDefinition(wa);
0320:
0321: // Make sure existing entities are refreshed with the most
0322: // recent PortletDefintion.
0323: Collection portletDefs = pa.getPortletDefinitions();
0324: if (portletDefs != null && portletDefs.size() > 0) {
0325: Iterator pdItr = portletDefs.iterator();
0326: while (pdItr.hasNext()) {
0327: PortletDefinition pd = (PortletDefinition) pdItr
0328: .next();
0329: Collection portletEntites = entityAccess
0330: .getPortletEntities(pd);
0331: if (portletEntites != null
0332: && portletEntites.size() > 0) {
0333: Iterator peItr = portletEntites.iterator();
0334: while (peItr.hasNext()) {
0335: PortletEntityCtrl portletEntity = (PortletEntityCtrl) peItr
0336: .next();
0337: portletEntity.setPortletDefinition(pd);
0338: }
0339: }
0340: }
0341: }
0342: } catch (Exception e) {
0343: String msg = "Failed to load portlet application for "
0344: + paWar.getPortletApplicationName();
0345: log.error(msg, e);
0346: throw new RegistryException(msg);
0347: }
0348:
0349: // register the portlet application
0350: try {
0351: registry.registerPortletApplication(pa);
0352: registered = true;
0353: log.info("Registered the portlet application " + paName);
0354:
0355: // add to search engine result
0356: this .updateSearchEngine(false, pa);
0357:
0358: // and add to the current node info
0359: nodeManager.addNode(new Long(pa.getId().toString()), pa
0360: .getName());
0361:
0362: // grant default permissions to portlet application
0363: grantDefaultPermissions(paName);
0364:
0365: if (autoCreateRoles
0366: && roleManager != null
0367: && pa.getWebApplicationDefinition()
0368: .getSecurityRoles() != null) {
0369: try {
0370: Iterator rolesIter = pa
0371: .getWebApplicationDefinition()
0372: .getSecurityRoles().iterator();
0373: SecurityRole sr;
0374: while (rolesIter.hasNext()) {
0375: sr = (SecurityRole) rolesIter.next();
0376: if (!roleManager.roleExists(sr.getRoleName())) {
0377: roleManager.addRole(sr.getRoleName());
0378: log.info("AutoCreated role: "
0379: + sr.getRoleName()
0380: + " from portlet application "
0381: + paName + " its web definition");
0382: }
0383: }
0384: } catch (SecurityException sex) {
0385: log.warn(
0386: "Failed to autoCreate roles for portlet application "
0387: + paName + ": " + sex.getMessage(),
0388: sex);
0389: }
0390: }
0391:
0392: return pa;
0393: } catch (Exception e) {
0394: String msg = "Failed to register portlet application, "
0395: + paName;
0396: log.error(msg, e);
0397:
0398: if (registered) {
0399: try {
0400: unregisterPortletApplication(pa,
0401: (paType == MutablePortletApplication.LOCAL));
0402: } catch (Exception re) {
0403: log.error(
0404: "Failed to rollback registration of portlet application "
0405: + paName, re);
0406: }
0407: }
0408:
0409: throw new RegistryException(msg, e);
0410: }
0411: }
0412:
0413: protected void startPA(String contextName, String contextPath,
0414: FileSystemHelper warStruct, ClassLoader paClassLoader,
0415: int paType) throws RegistryException {
0416: startPA(contextName, contextPath, warStruct, paClassLoader,
0417: paType, 0);
0418: }
0419:
0420: protected void startPA(String contextName, String contextPath,
0421: FileSystemHelper warStruct, ClassLoader paClassLoader,
0422: int paType, long checksum) throws RegistryException {
0423: boolean register = true;
0424: boolean monitored = false;
0425: DescriptorChangeMonitor changeMonitor = this .monitor;
0426: if (changeMonitor != null) {
0427: monitored = changeMonitor.isMonitored(contextName);
0428: }
0429: if (log.isDebugEnabled()) {
0430: log.debug("Is portlet application " + contextName
0431: + " monitored? -> " + monitored);
0432: }
0433: PortletApplicationWar paWar = null;
0434: try {
0435: if (log.isDebugEnabled()) {
0436: log.debug("Try to start portlet application "
0437: + contextName + ".");
0438: }
0439: // create PA from war (file) structure
0440: // paWar = new PortletApplicationWar(warStruct, contextName, "/" + contextName, checksum);
0441: paWar = new PortletApplicationWar(warStruct, contextName,
0442: contextPath, checksum);
0443: try {
0444: if (paClassLoader == null) {
0445: paClassLoader = paWar.createClassloader(getClass()
0446: .getClassLoader());
0447: }
0448: // create checksum from PA descriptors
0449: checksum = paWar.getPortletApplicationChecksum();
0450:
0451: if (log.isDebugEnabled()) {
0452: log.debug("New checksum for portlet application "
0453: + contextName + " is " + checksum);
0454: }
0455: } catch (IOException e) {
0456: String msg = "Invalid PA WAR for " + contextName;
0457: log.error(msg, e);
0458: if (paClassLoader == null) {
0459: // nothing to be done about it anymore: this pa is beyond repair :(
0460: throw new RegistryException(e);
0461: }
0462: register = false;
0463: }
0464:
0465: // try to get the PA from database by context name
0466: MutablePortletApplication pa = registry
0467: .getPortletApplication(contextName);
0468:
0469: if (pa != null) {
0470: if (log.isDebugEnabled()) {
0471: log.debug("Portlet Application " + contextName
0472: + " found in registry.");
0473: }
0474: if (pa.getApplicationType() != paType) {
0475: throw new RegistryException(
0476: "Cannot start portlet application "
0477: + contextName
0478: + ": as Application Types don't match: "
0479: + pa.getApplicationType() + " != "
0480: + paType);
0481: }
0482: if (!monitored && changeMonitor != null) {
0483: changeMonitor.remove(contextName);
0484: }
0485: if (log.isDebugEnabled()) {
0486: log.debug("unregistering portlet application "
0487: + contextName + "...");
0488: }
0489: portletFactory.unregisterPortletApplication(pa);
0490: }
0491: // if (register && (pa == null || checksum != pa.getChecksum()))
0492: if (register) {
0493: if (pa == null) {
0494: // new
0495: try {
0496: if (log.isDebugEnabled()) {
0497: log
0498: .debug("Register new portlet application "
0499: + contextName + ".");
0500: }
0501: pa = registerPortletApplication(paWar, pa,
0502: paType, paClassLoader);
0503: } catch (Exception e) {
0504: String msg = "Error register new portlet application "
0505: + contextName + ".";
0506:
0507: if (log.isDebugEnabled()) {
0508: log.debug(msg);
0509: }
0510: throw new RegistryException(msg);
0511:
0512: }
0513: } else {
0514: if (log.isDebugEnabled()) {
0515: log
0516: .debug("Re-register existing portlet application "
0517: + contextName + ".");
0518: }
0519: int status = nodeManager.checkNode(new Long(pa
0520: .getId().toString()), pa.getName());
0521: boolean reregister = false;
0522: boolean deploy = false;
0523: switch (status) {
0524: case NodeManager.NODE_NEW: {
0525: if (log.isDebugEnabled()) {
0526: log.debug("Node for Portlet application "
0527: + contextName + " is NEW.");
0528: }
0529: //only reason is that the file got somehow corrupted
0530: // so we really do not know what is going on here...
0531: // the best chance at this point is to reregister (which might be the absolute wrong choice)
0532: log
0533: .warn("The portlet application "
0534: + pa.getName()
0535: + " is registered in the database but not locally .... we will reregister");
0536: reregister = true;
0537: if (checksum != pa.getChecksum()) {
0538: log
0539: .warn("The provided portlet application "
0540: + pa.getName()
0541: + " is a different version than in the database (db-checksum="
0542: + pa.getChecksum()
0543: + ", local-checksum=: "
0544: + checksum
0545: + ") .... we will redeploy (also to the database)");
0546: deploy = true;
0547: }
0548: break;
0549: }
0550: case NodeManager.NODE_SAVED: {
0551: if (log.isDebugEnabled()) {
0552: log.debug("Node for Portlet application "
0553: + contextName + " is SAVED.");
0554: }
0555: if (checksum != pa.getChecksum()) {
0556: log
0557: .warn("The provided portlet application "
0558: + pa.getName()
0559: + " is a different version than in the local node info and the database (db-checksum="
0560: + pa.getChecksum()
0561: + ", local-checksum=: "
0562: + checksum
0563: + ") .... we will reregister AND redeploy (also to the database)");
0564: //database and local node info are in synch, so we assume that this is a brand new
0565: // war .... let's deploy
0566: reregister = true;
0567: deploy = true;
0568: }
0569: break;
0570: }
0571: case NodeManager.NODE_OUTDATED: {
0572: // new version in database, maybe changed by a different cluster node
0573: if (log.isDebugEnabled()) {
0574: log
0575: .debug("Node for Portlet application "
0576: + contextName
0577: + " is OUTDATED (local PA.id < DB PA.id).");
0578: }
0579: //database version is older (determined by id) than the database
0580: //let's deploy and reregister
0581: if (checksum != pa.getChecksum()) {
0582: log
0583: .error("The portlet application "
0584: + pa.getName()
0585: + " provided for the upgrade IS WRONG. The database checksum= "
0586: + pa.getChecksum()
0587: + ", but the local="
0588: + checksum
0589: + "....THIS NEEDS TO BE CORRECTED");
0590: // if the checksums do not match make sure the database is updated with the new PA from file system
0591: // I've observed "unavailable PA" in clustered env for the cluster node that reported OUTDATED state
0592: deploy = true;
0593: }
0594: reregister = true;
0595: break;
0596: }
0597: }
0598: if (deploy) {
0599: if (log.isDebugEnabled()) {
0600: log
0601: .debug("Register (deploy=true) Portlet application "
0602: + contextName
0603: + " in database.");
0604: }
0605: pa = registerPortletApplication(paWar, pa,
0606: paType, paClassLoader);
0607: } else if (reregister) {
0608: if (log.isDebugEnabled()) {
0609: log
0610: .debug("Re-Register (reregister=true) Portlet application "
0611: + contextName + ".");
0612: }
0613: // add to search engine result
0614: this .updateSearchEngine(true, pa);
0615: this .updateSearchEngine(false, pa);
0616:
0617: // and add to the current node info
0618: try {
0619: nodeManager.addNode(new Long(pa.getId()
0620: .toString()), pa.getName());
0621: } catch (Exception e) {
0622: log.error(
0623: "Adding node for portlet application "
0624: + pa.getName()
0625: + " caused exception", e);
0626: }
0627: }
0628:
0629: }
0630: }
0631: if (register) {
0632: if (log.isDebugEnabled()) {
0633: log.debug("Register Portlet application "
0634: + contextName + " in portlet factory.");
0635: }
0636: portletFactory.registerPortletApplication(pa,
0637: paClassLoader);
0638: }
0639:
0640: if (!monitored && changeMonitor != null) {
0641: if (log.isDebugEnabled()) {
0642: log.debug("Add change monitor for application "
0643: + contextName + " with checksum "
0644: + checksum + ".");
0645: }
0646: changeMonitor.monitor(contextName, contextPath,
0647: paClassLoader, paType, warStruct
0648: .getRootDirectory(), checksum);
0649: }
0650: } catch (Exception e) {
0651: String msg = "Error starting portlet application "
0652: + contextName;
0653:
0654: log.error(msg, e);
0655: // monitor PA for changes
0656: // do not add monitor if a monitor already exists
0657: if (!monitored && changeMonitor != null) {
0658: // this code should be hit only during startup process
0659: if (log.isDebugEnabled()) {
0660: log.debug("Add change monitor for application "
0661: + contextName
0662: + " and set unsuccessful starts to 1.");
0663: }
0664: changeMonitor.monitor(contextName, contextPath,
0665: paClassLoader, paType, warStruct
0666: .getRootDirectory(), checksum);
0667: changeMonitor.get(contextName).setUnsuccessfulStarts(1);
0668: }
0669: throw new RegistryException(msg);
0670: } finally {
0671: if (paWar != null) {
0672: try {
0673: paWar.close();
0674: } catch (IOException e) {
0675: log.error("Failed to close PA WAR for "
0676: + contextName, e);
0677: }
0678: }
0679: }
0680: }
0681:
0682: protected void stopPA(String contextName, int paType)
0683: throws RegistryException {
0684: MutablePortletApplication pa = null;
0685:
0686: try {
0687: pa = registry.getPortletApplication(contextName);
0688: } catch (Exception e) {
0689: // ignore errors during portal shutdown
0690: }
0691: if (pa != null && pa.getApplicationType() != paType) {
0692: throw new RegistryException(
0693: "Cannot stop portlet application " + contextName
0694: + ": as Application Types don't match: "
0695: + pa.getApplicationType() + " != " + paType);
0696: }
0697: DescriptorChangeMonitor monitor = this .monitor;
0698: if (monitor != null) {
0699: monitor.remove(contextName);
0700: }
0701: if (pa != null) {
0702: portletFactory.unregisterPortletApplication(pa);
0703: }
0704: }
0705:
0706: protected void updateSearchEngine(boolean remove,
0707: MutablePortletApplication pa) {
0708: if (searchEngine != null) {
0709: if (remove) {
0710: searchEngine.remove(pa);
0711: searchEngine.remove(pa.getPortletDefinitions());
0712: log
0713: .info("Un-Registered the portlet application in the search engine... "
0714: + pa.getName());
0715: } else {
0716: searchEngine.add(pa);
0717: searchEngine.add(pa.getPortletDefinitions());
0718: log
0719: .info("Registered the portlet application in the search engine... "
0720: + pa.getName());
0721: }
0722: }
0723:
0724: }
0725:
0726: protected void unregisterPortletApplication(
0727: MutablePortletApplication pa, boolean purgeEntityInfo)
0728: throws RegistryException {
0729:
0730: updateSearchEngine(true, pa);
0731: log
0732: .info("Remove all registry entries defined for portlet application "
0733: + pa.getName());
0734:
0735: Iterator portlets = pa.getPortletDefinitions().iterator();
0736:
0737: while (portlets.hasNext()) {
0738: PortletDefinition portletDefinition = (PortletDefinition) portlets
0739: .next();
0740: Iterator entities = entityAccess.getPortletEntities(
0741: portletDefinition).iterator();
0742:
0743: while (entities.hasNext()) {
0744: PortletEntity entity = (PortletEntity) entities.next();
0745:
0746: if (purgeEntityInfo) {
0747: try {
0748: entityAccess.removePortletEntity(entity);
0749: } catch (PortletEntityNotDeletedException e) {
0750: String msg = "Failed to delete Portlet Entity "
0751: + entity.getId();
0752: log.error(msg, e);
0753: throw new RegistryException(msg, e);
0754: }
0755: }
0756:
0757: entityAccess.removeFromCache(entity);
0758: windowAccess.removeWindows(entity);
0759: }
0760: }
0761:
0762: // todo keep (User)Prefs?
0763: registry.removeApplication(pa);
0764: revokeDefaultPermissions(pa.getName());
0765: }
0766:
0767: protected void grantDefaultPermissions(String paName) {
0768: try {
0769: // create a default permission for this portlet app, granting configured roles to the portlet application
0770: Iterator roles = permissionRoles.iterator();
0771: while (roles.hasNext()) {
0772: String roleName = (String) roles.next();
0773: Role userRole = roleManager.getRole(roleName);
0774: if (userRole != null) {
0775: Permission permission = new PortletPermission(
0776: paName + "::*", "view, edit");
0777: if (!permissionManager.permissionExists(permission)) {
0778: permissionManager.addPermission(permission);
0779: permissionManager.grantPermission(userRole
0780: .getPrincipal(), permission);
0781: }
0782: }
0783: }
0784: } catch (SecurityException e) {
0785: log.error("Error granting default permissions for "
0786: + paName, e);
0787: }
0788: }
0789:
0790: protected void revokeDefaultPermissions(String paName) {
0791: try {
0792: Iterator roles = permissionRoles.iterator();
0793: while (roles.hasNext()) {
0794: String roleName = (String) roles.next();
0795: Role userRole = roleManager.getRole(roleName);
0796: if (userRole != null) {
0797: Permission permission = new PortletPermission(
0798: paName + "::*", "view, edit");
0799: if (permissionManager.permissionExists(permission)) {
0800: permissionManager.removePermission(permission);
0801: }
0802:
0803: }
0804: }
0805: } catch (SecurityException e) {
0806: log.error("Error revoking default permissions for "
0807: + paName, e);
0808: }
0809: }
0810:
0811: public int getDescriptorChangeMonitorInterval() {
0812: return descriptorChangeMonitorInterval / 1000;
0813: }
0814:
0815: public void setDescriptorChangeMonitorInterval(
0816: int descriptorChangeMonitorInterval) {
0817: this .descriptorChangeMonitorInterval = descriptorChangeMonitorInterval * 1000;
0818: }
0819:
0820: private static class DescriptorChangeMonitor extends Thread {
0821: private static class DescriptorChangeMonitorInfo {
0822: private String contextName;
0823: private String contextPath;
0824: private ClassLoader paClassLoader;
0825: private int paType;
0826: private File paDir;
0827: private File[] descriptors;
0828: private long descriptorModificationTime;
0829: private long extendedDescriptorModificationTime;
0830: private long checksum;
0831: private boolean obsolete;
0832:
0833: /**
0834: * holds the number of unsuccessful starts of the monitored PA
0835: */
0836: private int unsuccessfulStarts;
0837:
0838: /*
0839: * Constructor only used for looking up the matching registered one in monitorsInfo
0840: */
0841: public DescriptorChangeMonitorInfo(String contextName) {
0842: this .contextName = contextName;
0843: }
0844:
0845: public DescriptorChangeMonitorInfo(String contextName,
0846: String contextPath, ClassLoader paClassLoader,
0847: int paType, File paDir, long checksum) {
0848: this .contextName = contextName;
0849: this .contextPath = contextPath;
0850: this .paClassLoader = paClassLoader;
0851: this .paType = paType;
0852: this .paDir = paDir.isAbsolute() ? paDir : paDir
0853: .getAbsoluteFile();
0854: this .checksum = checksum;
0855:
0856: this .descriptors = new File[] {
0857: new File(paDir,
0858: PortletApplicationWar.WEB_XML_PATH),
0859: new File(paDir,
0860: PortletApplicationWar.PORTLET_XML_PATH),
0861: new File(
0862: paDir,
0863: PortletApplicationWar.EXTENDED_PORTLET_XML_PATH) };
0864:
0865: descriptorModificationTime = descriptors[1]
0866: .lastModified();
0867: extendedDescriptorModificationTime = descriptors[2]
0868: .lastModified();
0869: }
0870:
0871: public String getContextName() {
0872: return contextName;
0873: }
0874:
0875: public ClassLoader getPAClassLoader() {
0876: return paClassLoader;
0877: }
0878:
0879: public int getPortletApplicationType() {
0880: return paType;
0881: }
0882:
0883: public File getPADir() {
0884: return paDir;
0885: }
0886:
0887: public long getChecksum() {
0888: return checksum;
0889: }
0890:
0891: public boolean isChanged() {
0892: if (!obsolete) {
0893: long newDescriptorModificationTime = descriptors[1]
0894: .lastModified();
0895: long newExtendedDescriptorModificationTime = descriptors[2]
0896: .lastModified();
0897: if (descriptorModificationTime != newDescriptorModificationTime
0898: || extendedDescriptorModificationTime != newExtendedDescriptorModificationTime) {
0899: descriptorModificationTime = newDescriptorModificationTime;
0900: extendedDescriptorModificationTime = newExtendedDescriptorModificationTime;
0901: long newChecksum = MultiFileChecksumHelper
0902: .getChecksum(descriptors);
0903: if (log.isDebugEnabled()) {
0904: log
0905: .debug("checksum check for descriptors for application "
0906: + contextName
0907: + ": old ("
0908: + checksum
0909: + ") new ("
0910: + newChecksum + ").");
0911: }
0912: if (checksum != newChecksum) {
0913: if (log.isDebugEnabled()) {
0914: log
0915: .debug("portlet descriptors for application "
0916: + contextName
0917: + " have changed.");
0918: }
0919: checksum = newChecksum;
0920: // reset this to restart unsuccessful PA start handling for evers PA descriptor change
0921: unsuccessfulStarts = 0;
0922: return true;
0923: }
0924: }
0925: }
0926: return false;
0927: }
0928:
0929: public void setObsolete() {
0930: obsolete = true;
0931: }
0932:
0933: public boolean isObsolete() {
0934: return obsolete;
0935: }
0936:
0937: public int getUnsuccessfulStarts() {
0938: return unsuccessfulStarts;
0939: }
0940:
0941: public void setUnsuccessfulStarts(int unsuccessfulStarts) {
0942: this .unsuccessfulStarts = unsuccessfulStarts;
0943: }
0944:
0945: public String getContextPath() {
0946: return contextPath;
0947: }
0948: }
0949:
0950: private PortletApplicationManager pam;
0951: private long interval;
0952: private boolean started = true;
0953: private ArrayList monitorInfos;
0954: private int maxRetriedStarts;
0955:
0956: public DescriptorChangeMonitor(ThreadGroup group, String name,
0957: PortletApplicationManager pam, long interval,
0958: int maxretriedStarts) {
0959: super (group, name);
0960: this .pam = pam;
0961: this .interval = interval;
0962: monitorInfos = new ArrayList();
0963: setPriority(MIN_PRIORITY);
0964: setDaemon(true);
0965: this .maxRetriedStarts = maxretriedStarts;
0966: }
0967:
0968: public void run() {
0969: try {
0970: sleep(interval);
0971: } catch (InterruptedException e) {
0972: }
0973: while (started) {
0974: checkDescriptorChanges();
0975:
0976: try {
0977: sleep(interval);
0978: } catch (InterruptedException e) {
0979:
0980: }
0981: }
0982: }
0983:
0984: /**
0985: * notifies a switch variable that exits the watcher's montior loop started in the <code>run()</code> method.
0986: */
0987: public synchronized void safeStop() {
0988: started = false;
0989: monitorInfos.clear();
0990: }
0991:
0992: public synchronized void monitor(String contextName,
0993: String contextPath, ClassLoader paClassLoader,
0994: int paType, File paDir, long checksum) {
0995: monitorInfos.add(new DescriptorChangeMonitorInfo(
0996: contextName, contextPath, paClassLoader, paType,
0997: paDir, checksum));
0998: }
0999:
1000: public synchronized void remove(String contextName) {
1001: DescriptorChangeMonitorInfo monitorInfo;
1002: for (int i = monitorInfos.size() - 1; i > -1; i--) {
1003: monitorInfo = (DescriptorChangeMonitorInfo) monitorInfos
1004: .get(i);
1005: if (contextName.equals(monitorInfo.getContextName())) {
1006: // will be removed by checkDescriptorChanges on next iteration
1007: monitorInfo.setObsolete();
1008: break;
1009: }
1010: }
1011: }
1012:
1013: public synchronized DescriptorChangeMonitorInfo get(
1014: String contextName) {
1015: DescriptorChangeMonitorInfo monitorInfo;
1016: for (int i = monitorInfos.size() - 1; i > -1; i--) {
1017: monitorInfo = (DescriptorChangeMonitorInfo) monitorInfos
1018: .get(i);
1019: if (contextName.equals(monitorInfo.getContextName())) {
1020: return monitorInfo;
1021: }
1022: }
1023: return null;
1024: }
1025:
1026: public boolean isMonitored(String contextName) {
1027: DescriptorChangeMonitorInfo monitorInfo = this
1028: .get(contextName);
1029: if (monitorInfo != null && !monitorInfo.isObsolete()) {
1030: return true;
1031: }
1032: return false;
1033: }
1034:
1035: private void checkDescriptorChanges() {
1036: int size;
1037: synchronized (this ) {
1038: size = monitorInfos.size();
1039: }
1040:
1041: if (log.isDebugEnabled()) {
1042: log
1043: .debug("check for portlet application descriptor changes.");
1044: }
1045:
1046: for (int i = size - 1; i > -1; i--) {
1047: DescriptorChangeMonitorInfo monitorInfo;
1048: synchronized (this ) {
1049: if (started) {
1050: monitorInfo = (DescriptorChangeMonitorInfo) monitorInfos
1051: .get(i);
1052: if (monitorInfo.isObsolete()) {
1053: monitorInfos.remove(i);
1054: } else {
1055: try {
1056: int unsuccessfulStarts = monitorInfo
1057: .getUnsuccessfulStarts();
1058: // try to restart PA if the PA-descriptors have change
1059: // OR (if we encountered an exception while starting the PA)
1060: // keep on trying to restart PA until maxRetriedStarts is reached
1061: // This ensures we finally startup in a clustered enviroment, where parallel registration
1062: // of PAs could lead to contraint violation eceptions in DB.
1063: // see https://issues.apache.org/jira/browse/JS2-666
1064: // Note: monitorInfo.isChanged() will reset unsuccessfulStarts to 0 if a PA descriptor change
1065: // has been detected (monitorInfo.isChanged() == true).
1066: if (monitorInfo.isChanged()
1067: || (unsuccessfulStarts > 0 && unsuccessfulStarts <= maxRetriedStarts)) {
1068: try {
1069: pam
1070: .startPA(
1071: monitorInfo
1072: .getContextName(),
1073: monitorInfo
1074: .getContextPath(),
1075: new DirectoryHelper(
1076: monitorInfo
1077: .getPADir()),
1078: monitorInfo
1079: .getPAClassLoader(),
1080: monitorInfo
1081: .getPortletApplicationType(),
1082: monitorInfo
1083: .getChecksum());
1084: // great! we have a successful start. set unsuccessful starts to 0
1085: monitorInfo
1086: .setUnsuccessfulStarts(0);
1087: } catch (Exception e) {
1088: if (monitorInfo.isChanged()) {
1089: log
1090: .error(
1091: "Failed to restart PortletApplication "
1092: + monitorInfo
1093: .getContextName(),
1094: e);
1095: } else if (log.isWarnEnabled()) {
1096: log
1097: .warn(
1098: "Failed to restart PortletApplication "
1099: + monitorInfo
1100: .getContextName(),
1101: e);
1102: }
1103: // we encountered an error while starting the PA
1104: // this could result from clustered environments problems (see above)
1105: // increase unsuccessfulStarts until the maxRetriedStarts is reached
1106: monitorInfo
1107: .setUnsuccessfulStarts(unsuccessfulStarts + 1);
1108: if (log.isDebugEnabled()) {
1109: log
1110: .debug("Number of unsuccessful PA starts is "
1111: + monitorInfo
1112: .getUnsuccessfulStarts()
1113: + ".");
1114: }
1115: if (monitorInfo
1116: .getUnsuccessfulStarts() > maxRetriedStarts) {
1117: log
1118: .error("Max number of retries ("
1119: + maxRetriedStarts
1120: + ") reached. Ignoring Monitor for "
1121: + monitorInfo
1122: .getContextName());
1123: }
1124: }
1125: }
1126: } catch (Exception e) {
1127: // ignore filesystem and/or descriptor errors, maybe next time round they'll be fixed again
1128: log
1129: .error(
1130: "Descriptor Change check failure for PortletApplication "
1131: + monitorInfo
1132: .getContextName(),
1133: e);
1134: }
1135: }
1136: }
1137: }
1138: }
1139: }
1140: }
1141:
1142: public void setMaxRetriedStarts(int maxRetriedStarts) {
1143: this .maxRetriedStarts = maxRetriedStarts;
1144: }
1145:
1146: public int getMaxRetriedStarts() {
1147: return maxRetriedStarts;
1148: }
1149: }
|