/* * 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.server.webapp; import java.util.ArrayList; import java.util.Comparator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import com.caucho.config.ConfigException; import com.caucho.env.deploy.DeployControllerApi; import com.caucho.env.deploy.DeployControllerType; import com.caucho.env.deploy.ExpandVersion; import com.caucho.util.Alarm; import com.caucho.util.L10N; /** * A configuration entry for a versioning web-app. */ public class WebAppVersioningController extends WebAppController { private static final L10N L = new L10N(WebAppVersioningController.class); private static final Logger log = Logger.getLogger(WebAppController.class.getName()); private static final long EXPIRE_PERIOD = 3600 * 1000L; private final String _baseKey; private long _versionRolloverTime = EXPIRE_PERIOD; private ArrayList<WebAppController> _controllerList = new ArrayList<WebAppController>(); private final WebAppExpandDeployGenerator _generator; private long _restartTime; private ExpandVersion _primaryVersion; private WebAppController _primaryController; private ArrayList<DeployControllerApi<WebApp>> _mergeList = new ArrayList<DeployControllerApi<WebApp>>(); private boolean _isModified = false; private AtomicBoolean _isUpdating = new AtomicBoolean(); public WebAppVersioningController(String id, String baseKey, String contextPath, WebAppExpandDeployGenerator generator, WebAppContainer container) { super(id + "-0.0.0.versioning", null, container, contextPath); _baseKey = baseKey; _generator = generator; } void setModified(boolean isModified) { _isModified = isModified; } @Override public boolean isVersioning() { return true; } @Override public DeployControllerType getControllerType() { return DeployControllerType.VERSIONING; } @Override public String getVersion() { if (_primaryController != null) return _primaryController.getVersion(); else return ""; } @Override public WebAppAdmin getAdmin() { if (_primaryController != null) return _primaryController.getAdmin(); else return null; } /** * Returns the instance for a top-level request * * @return the request object or null for none. */ @Override public WebApp instantiateDeployInstance() { updateVersion(); WebAppController controller = _primaryController; if (controller != null) { WebApp webApp = controller.request(); return webApp; } else throw new NullPointerException(getClass().getName()); } /** * Starts the entry. */ @Override protected WebApp startImpl() { // server/1h2g // super.startImpl(); updateVersion(); WebAppController controller = _primaryController; if (controller != null) return controller.request(); else return null; } @Override public WebApp getDeployInstance() { WebAppController controller = _primaryController; if (controller != null) return controller.request(); else return null; } @Override protected void stopImpl() { } @Override protected void stopLazyImpl() { } @Override protected void destroyInstance(WebApp instance) { } /** * Initialize the controller. */ @Override protected void initBegin() { /* super.initBegin(); */ } /** * Initialize the controller. */ @Override protected void initEnd() { /* super.initBegin(); */ } @Override public boolean isModified() { boolean isModified = updateVersion(); return isModified; } @Override public void merge(DeployControllerApi<WebApp> newControllerV) { _mergeList.add(newControllerV); if (_primaryController != null) _primaryController.merge(newControllerV); else { // server/12ab super.merge(newControllerV); } } public boolean updateVersion() { // _isModified = true; return updateVersionImpl(); } private boolean updateVersionImpl() { if (! _isUpdating.compareAndSet(false, true)) return false; try { synchronized (this) { ExpandVersion oldPrimaryVersion = _primaryVersion; WebAppController oldPrimaryController = _primaryController; WebAppController newPrimaryController = null; ExpandVersion version = _generator.getPrimaryVersion(_baseKey); if (oldPrimaryVersion != null && oldPrimaryVersion.equals(version)) return false; if (version != null) { newPrimaryController = _generator.createVersionController(version); newPrimaryController.merge(newPrimaryController); } if (newPrimaryController == null) { throw new ConfigException(L.l(this + " does not have an implementing version")); } if (newPrimaryController == oldPrimaryController) return false; log.fine(this + " updating primary to " + newPrimaryController); if (oldPrimaryController != null && oldPrimaryController != newPrimaryController) { _controllerList.add(oldPrimaryController); } // server/1h20 newPrimaryController.init(); newPrimaryController.setVersionAlias(true); // server/12ab newPrimaryController.merge(this); // server/1h35 for (DeployControllerApi<WebApp> newController : _mergeList) { newPrimaryController.merge(newController); } _primaryController = newPrimaryController; _primaryVersion = version; _controllerList.remove(newPrimaryController); int size = _controllerList.size(); if (size > 0) { WebAppController oldController = _controllerList.get(size - 1); long expireTime = Alarm.getCurrentTime() + _versionRolloverTime; _primaryController.setOldWebApp(oldController, expireTime); } _restartTime = Alarm.getCurrentTime(); clearCache(); return true; } } finally { _isUpdating.set(false); } } /** * Returns a printable view. */ @Override public String toString() { return getClass().getSimpleName() + "[" + getId() + "]"; } static class VersionNameComparator implements Comparator<String> { public int compare(String versionA, String versionB) { /* String versionA = a.getVersion(); String versionB = b.getVersion(); */ int lengthA = versionA.length(); int lengthB = versionB.length(); int indexA = 0; int indexB = 0; while (indexA < lengthA && indexB < lengthB) { int valueA = 0; int valueB = 0; char chA; char chB; for (; indexA < lengthA && '0' <= (chA = versionA.charAt(indexA)) && chA <= '9'; indexA++) { valueA = 10 * valueA + chA - '0'; } for (; indexB < lengthB && '0' <= (chB = versionB.charAt(indexB)) && chB <= '9'; indexB++) { valueB = 10 * valueB + chB - '0'; } if (valueA < valueB) return 1; else if (valueB < valueA) return -1; while (indexA < lengthA && indexB < lengthB && ! ('0' <= (chA = versionA.charAt(indexA)) && chA <= '9') && ! ('0' <= (chB = versionB.charAt(indexB)) && chB <= '9')) { if (chA < chB) return 1; else if (chB < chA) return -1; indexA++; indexB++; } if (indexA < lengthA && ! ('0' <= (chA = versionA.charAt(indexA)) && chA <= '9')) return 1; else if (indexB < lengthB && ! ('0' <= (chB = versionB.charAt(indexB)) && chB <= '9')) return -1; } if (indexA != lengthA) return 1; else if (indexB != lengthB) return -1; else return 0; } } }