/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.env.deploy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import java.util.TreeSet; import java.util.logging.Logger; import javax.annotation.PostConstruct; import com.caucho.lifecycle.Lifecycle; import com.caucho.loader.Environment; import com.caucho.make.CachedDependency; import com.caucho.util.ConcurrentArrayList; import com.caucho.util.ConcurrentArrayList.Match; import com.caucho.vfs.Dependency; /** * A container of deploy objects. */ public class DeployContainer<C extends DeployControllerApi<?>> extends CachedDependency implements DeployContainerApi<C>, Dependency { private final DeployListGenerator<C> _deployListGenerator = new DeployListGenerator<C>(this); private final ConcurrentArrayList<C> _controllerList; private final Lifecycle _lifecycle = new Lifecycle(); /** * Creates the deploy container. */ public DeployContainer(Class<C> type) { _controllerList = new ConcurrentArrayList<C>(type); setCheckInterval(Environment.getDependencyCheckInterval()); } /** * Adds a deploy generator. */ @Override public void add(DeployGenerator<C> generator) { Set<String> names = new TreeSet<String>(); generator.fillDeployedNames(names); _deployListGenerator.add(generator); if (_lifecycle.isActive()) update(names); } /** * Removes a deploy. */ @Override public void remove(DeployGenerator<C> generator) { Set<String> names = new TreeSet<String>(); generator.fillDeployedNames(names); _deployListGenerator.remove(generator); if (_lifecycle.isActive()) update(names); } /** * Returns true if the deployment has modified. */ @Override public boolean isModifiedImpl() { return _deployListGenerator.isModified(); } /** * Logs the reason for modification. */ @Override public boolean logModified(Logger log) { return _deployListGenerator.logModified(log); } /** * Forces updates. */ @Override public void update() { _deployListGenerator.update(); } /** * Initialize the container. */ @PostConstruct public void init() { if (! _lifecycle.toInit()) return; } /** * Start the container. */ @Override public void start() { init(); if (! _lifecycle.toActive()) return; _deployListGenerator.start(); HashSet<String> keys = new LinkedHashSet<String>(); _deployListGenerator.fillDeployedNames(keys); for (String key : keys) { updateImpl(key); } ArrayList<C> controllerList = new ArrayList<C>(_controllerList); Collections.sort(controllerList, new StartupPriorityComparator()); for (int i = 0; i < controllerList.size(); i++) { C controller = controllerList.get(i); controller.startOnInit(); } } /** * Returns the matching entry. */ @Override public C findController(String name) { C controller = findDeployedController(name); if (controller != null) return controller; controller = generateController(name); if (controller == null) return null; // server/10tm else if (controller.isNameMatch(name)) { return controller; } else { return null; } } /** * Returns the matching entry. */ public C findControllerById(String name) { return findDeployedControllerById(name); } /** * Returns the deployed entries. */ public C []getControllers() { return _controllerList.toArray(); } /** * Updates all the names. */ private void update(Set<String> names) { Iterator<String> iter = names.iterator(); while (iter.hasNext()) { String name = iter.next(); update(name); } } /** * Callback from the DeployGenerator when the deployment changes. * <code>update</code> is only called when a deployment is added * or removed, e.g. with a new .war file. * * The entry handles its own internal changes, e.g. a modification to * a web.xml file. */ @Override public C update(String name) { C newController = updateImpl(name); if (_lifecycle.isActive() && newController != null) newController.startOnInit(); return newController; } /** * Callback from the DeployGenerator when the deployment changes. * <code>update</code> is only called when a deployment is added * or removed, e.g. with a new .war file. * * The entry handles its own internal changes, e.g. a modification to * a web.xml file. */ public C updateNoStart(String name) { C newController = updateImpl(name); return newController; } /** * Callback from the DeployGenerator when the deployment changes. * <code>update</code> is only called when a deployment is added * or removed, e.g. with a new .war file. * * The entry handles its own internal changes, e.g. a modification to * a web.xml file. */ C updateImpl(String name) { remove(name); // destroy must be before generate because of JMX unregistration C newController = generateController(name); return newController; } /** * Called to explicitly remove an entry from the cache. */ @Override public void remove(String name) { C oldController = _controllerList.remove(name, getControllerNameMatch()); if (oldController != null) oldController.close(); } /** * Generates the controller. */ private C generateController(String name) { // XXX: required for watchdog /* if (! _lifecycle.isActive()) return null; */ ArrayList<C> controllerList = new ArrayList<C>(); _deployListGenerator.generateController(name, controllerList); C bestController = null; for (C controller : controllerList) { if (bestController == null) bestController = controller; else if (controller.getControllerType().ordinal() < bestController.getControllerType().ordinal()) { bestController = controller; } } if (bestController == null) return null; // server/1h8j for (C controller : controllerList) { if (controller != bestController) { bestController.merge((DeployControllerApi) controller); } } // server/1h10 _deployListGenerator.mergeController(bestController, name); return addController(bestController); } private C addController(C newController) { // server/1h00,13g4 // generated controller might match the name, e.g. // when webapps deploy has an overriding explicit <web-app> if (newController == null) return null; C oldController = null; // the new entry might already be generated by another thread synchronized (_controllerList) { oldController = findDeployedControllerById(newController.getId()); if (oldController == null) { _controllerList.add(newController); } } if (oldController != null) { // if (controller.isVersioning()) // controller.updateVersion(); oldController.update(); return oldController; } else { init(newController); return newController; } } private void init(C controller) { controller.init(); } /** * Returns an already deployed entry. */ private C findDeployedController(String name) { return _controllerList.find(name, getControllerNameMatch()); } /** * Returns an already deployed entry. */ private C findDeployedControllerById(String id) { return _controllerList.find(id, getControllerIdMatch()); } /** * Closes the stop. */ public void stop() { if (! _lifecycle.toStop()) return; ArrayList<C> controllers = new ArrayList<C>(_controllerList); Collections.sort(controllers, new StartupPriorityComparator()); for (int i = controllers.size() - 1; i >= 0; i--) controllers.get(i).stop(); } /** * Closes the deploys. */ public void destroy() { stop(); if (! _lifecycle.toDestroy()) return; _deployListGenerator.destroy(); ArrayList<C> controllerList = new ArrayList<C>(_controllerList); _controllerList.clear(); Collections.sort(controllerList, new StartupPriorityComparator()); for (int i = controllerList.size() - 1; i >= 0; i--) { C controller = controllerList.get(i); controller.close(); } } @SuppressWarnings("unchecked") private Match<C,String> getControllerNameMatch() { return (Match<C,String>) ControllerNameMatch.MATCH; } @SuppressWarnings("unchecked") private Match<C,String> getControllerIdMatch() { return (Match<C,String>) ControllerIdMatch.MATCH; } @Override public String toString() { return "DeployContainer$" + System.identityHashCode(this) + "[]"; } public class StartupPriorityComparator implements Comparator<C> { public int compare(C a, C b) { if (a.getStartupPriority() == b.getStartupPriority()) return 0; else if (a.getStartupPriority() < b.getStartupPriority()) return -1; else return 1; } } static class ControllerNameMatch<C extends DeployControllerApi<?>> implements Match<C,String> { static final ControllerNameMatch<DeployControllerApi<?>> MATCH = new ControllerNameMatch<DeployControllerApi<?>>(); @Override public boolean isMatch(C controller, String name) { return controller.isNameMatch(name); } } static class ControllerIdMatch<C extends DeployControllerApi<?>> implements Match<C,String> { static final ControllerIdMatch<DeployControllerApi<?>> MATCH = new ControllerIdMatch<DeployControllerApi<?>>(); @Override public boolean isMatch(C controller, String id) { return controller.getId().equals(id); } } }