/* * 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.admin; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import javax.annotation.PostConstruct; import javax.management.ObjectName; import com.caucho.bam.BamError; import com.caucho.bam.Query; import com.caucho.bam.actor.SimpleActor; import com.caucho.bam.mailbox.MultiworkerMailbox; import com.caucho.cloud.bam.BamSystem; import com.caucho.cloud.deploy.CopyTagQuery; import com.caucho.cloud.deploy.RemoveTagQuery; import com.caucho.cloud.deploy.SetTagQuery; import com.caucho.config.ConfigException; import com.caucho.config.Service; import com.caucho.env.deploy.DeployControllerService; import com.caucho.env.deploy.DeployTagItem; import com.caucho.env.repository.RepositorySystem; import com.caucho.env.repository.RepositorySpi; import com.caucho.env.repository.RepositoryTagEntry; import com.caucho.jmx.Jmx; import com.caucho.management.server.DeployControllerMXBean; import com.caucho.management.server.EAppMXBean; import com.caucho.management.server.EarDeployMXBean; import com.caucho.management.server.WebAppDeployMXBean; import com.caucho.management.server.WebAppMXBean; import com.caucho.server.cluster.Server; import com.caucho.server.host.HostController; import com.caucho.server.webapp.WebAppContainer; import com.caucho.server.webapp.WebAppController; import com.caucho.util.IoUtil; import com.caucho.util.L10N; import com.caucho.vfs.Path; import com.caucho.vfs.Vfs; public class DeployActor extends SimpleActor { private static final Logger log = Logger.getLogger(DeployActor.class.getName()); private static final L10N L = new L10N(DeployActor.class); private Server _server; private RepositorySpi _repository; private AtomicBoolean _isInit = new AtomicBoolean(); public DeployActor() { super("deploy@resin.caucho", BamSystem.getCurrentBroker()); } /* @Override public ManagedBroker getBroker() { return _server.getAdminBroker(); } */ @PostConstruct public void init() { if (_isInit.getAndSet(true)) return; _server = Server.getCurrent(); if (_server == null) throw new ConfigException(L.l("resin:DeployService requires an active Server.\n {0}", Thread.currentThread().getContextClassLoader())); _repository = RepositorySystem.getCurrentRepositorySpi(); setBroker(getBroker()); MultiworkerMailbox mailbox = new MultiworkerMailbox(getActor().getAddress(), getActor(), getBroker(), 2); getBroker().addMailbox(mailbox); } @Query public boolean commitList(long id, String to, String from, DeployCommitListQuery commitList) { ArrayList<String> uncommittedList = new ArrayList<String>(); if (commitList.getCommitList() != null) { for (String commit : commitList.getCommitList()) { if (! _repository.exists(commit)) uncommittedList.add(commit); } } DeployCommitListQuery resultList = new DeployCommitListQuery(uncommittedList); getBroker().queryResult(id, from, to, resultList); return true; } @Query public void tagCopy(long id, String to, String from, CopyTagQuery query) { String tag = query.getTag(); String sourceTag = query.getSourceTag(); RepositoryTagEntry entry = _repository.getTagMap().get(sourceTag); if (entry == null) { log.fine(this + " copyError dst='" + query.getTag() + "' src='" + query.getSourceTag() + "'"); getBroker().queryError(id, from, to, query, new BamError(BamError.TYPE_CANCEL, BamError.ITEM_NOT_FOUND, "unknown tag")); return; } log.fine(this + " copy dst='" + query.getTag() + "' src='" + query.getSourceTag() + "'"); String server = "default"; TreeMap<String,String> metaDataMap = new TreeMap<String,String>(); if (query.getAttributes() != null) metaDataMap.putAll(query.getAttributes()); if (server != null) metaDataMap.put("server", server); boolean result = _repository.putTag(tag, entry.getRoot(), metaDataMap); getBroker().queryResult(id, from, to, result); } @Query public void tagState(long id, String to, String from, TagStateQuery query) { // XXX: just ping the tag? // updateDeploy(); String tag = query.getTag(); DeployControllerService deploy = DeployControllerService.getCurrent(); DeployTagItem item = null; if (deploy != null) { deploy.update(tag); item = deploy.getTagItem(tag); } if (item != null) { TagStateQuery result = new TagStateQuery(tag, item.getState(), item.getDeployException()); getBroker().queryResult(id, from, to, result); } else getBroker().queryResult(id, from, to, null); } @Query public Boolean removeTag(RemoveTagQuery query) { if (log.isLoggable(Level.FINE)) log.fine(this + " query " + query); String server = "default"; HashMap<String,String> commitMetaData = new HashMap<String,String>(); if (query.getAttributes() != null) commitMetaData.putAll(query.getAttributes()); commitMetaData.put("server", server); return _repository.removeTag(query.getTag(), commitMetaData); } @Query public boolean sendFileQuery(long id, String to, String from, DeploySendQuery query) { String sha1 = query.getSha1(); if (log.isLoggable(Level.FINER)) log.finer(this + " sendFileQuery sha1=" + sha1); InputStream is = null; try { is = query.getInputStream(); _repository.writeRawGitFile(sha1, is); getBroker().queryResult(id, from, to, true); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); getBroker().queryResult(id, from, to, false); } finally { IoUtil.close(is); } return true; } @Query public boolean setTagQuery(long id, String to, String from, SetTagQuery query) { String tagName = query.getTag(); String contentHash = query.getContentHash(); if (contentHash == null) throw new NullPointerException(); String server = "default"; TreeMap<String,String> commitMetaData = new TreeMap<String,String>(); if (query.getAttributes() != null) commitMetaData.putAll(query.getAttributes()); commitMetaData.put("server", server); boolean result = _repository.putTag(tagName, contentHash, commitMetaData); getBroker().queryResult(id, from, to, String.valueOf(result)); return true; } @Query public boolean queryTags(long id, String to, String from, QueryTagsQuery tagsQuery) { ArrayList<TagResult> tags = new ArrayList<TagResult>(); Pattern pattern = Pattern.compile(tagsQuery.getPattern()); for (Map.Entry<String, RepositoryTagEntry> entry : _repository.getTagMap().entrySet()) { String tag = entry.getKey(); if (pattern.matcher(tag).matches()) tags.add(new TagResult(tag, entry.getValue().getRoot())); } getBroker() .queryResult(id, from, to, tags.toArray(new TagResult[tags.size()])); return true; } /** * @deprecated */ @Query public boolean controllerDeploy(long id, String to, String from, ControllerDeployQuery query) { String status = deploy(query.getTag()); log.fine(this + " deploy '" + query.getTag() + "' -> " + status); getBroker().queryResult(id, from, to, true); return true; } private String deploy(String gitPath) { return start(gitPath); } /** * @deprecated */ @Query public boolean controllerStart(long id, String to, String from, ControllerStartQuery query) { String status = start(query.getTag()); log.fine(this + " start '" + query.getTag() + "' -> " + status); getBroker().queryResult(id, from, to, true); return true; } private String start(String tag) { DeployControllerService service = DeployControllerService.getCurrent(); DeployTagItem controller = service.getTagItem(tag); if (controller == null) return L.l("'{0}' is an unknown controller", controller); try { controller.toStart(); return controller.getState(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); return e.toString(); } } /** * @deprecated */ @Query public boolean controllerStop(long id, String to, String from, ControllerStopQuery query) { String status = stop(query.getTag()); log.fine(this + " stop '" + query.getTag() + "' -> " + status); getBroker().queryResult(id, from, to, true); return true; } private String stop(String tag) { DeployControllerService service = DeployControllerService.getCurrent(); DeployTagItem controller = service.getTagItem(tag); if (controller == null) return L.l("'{0}' is an unknown controller", controller); try { controller.toStop(); return controller.getState(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); return e.toString(); } } @Query public boolean controllerRestart(long id, String to, String from, ControllerRestartQuery query) { String status = restart(query.getTag()); log.fine(this + " restart '" + query.getTag() + "' -> " + status); getBroker().queryResult(id, from, to, true); return true; } private String restart(String tag) { DeployControllerService service = DeployControllerService.getCurrent(); DeployTagItem controller = service.getTagItem(tag); if (controller == null) return L.l("'{0}' is an unknown controller", controller); try { controller.toRestart(); return controller.getState(); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); return e.toString(); } } /** * @deprecated */ @Query public boolean controllerUndeploy(long id, String to, String from, ControllerUndeployQuery query) { String status = undeploy(query.getTag()); log.fine(this + " undeploy '" + query.getTag() + "' -> " + status); getBroker().queryResult(id, from, to, true); return true; } /** * @deprecated */ private String undeploy(String tag) { DeployControllerMXBean controller = findController(tag); if (controller == null) return L.l("'{0}' is an unknown controller", controller); try { Path root = Vfs.lookup(controller.getRootDirectory()); root.removeAll(); controller.stop(); if (controller.destroy()) return "undeployed"; else return L.l("'{0}' failed to undeploy application '{1}'", controller, tag); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); return e.toString(); } } /** * @deprecated */ @Query public boolean controllerUndeploy(long id, String to, String from, UndeployQuery query) { if (true) throw new UnsupportedOperationException(); String status = null;//undeploy(query.getTag(), query.getUser(), query.getMessage()); log.fine(this + " undeploy '" + query.getTag() + "' -> " + status); getBroker().queryResult(id, from, to, true); return true; } private String undeploy(String tag, Map<String,String> commitMetaData) { DeployControllerMXBean controller = findController(tag); if (controller == null) return L.l("'{0}' is an unknown controller", controller); try { Path root = Vfs.lookup(controller.getRootDirectory()); root.removeAll(); controller.stop(); if (controller.destroy()) { String server = "default"; HashMap<String,String> metaDataCopy = new HashMap<String,String>(); if (commitMetaData != null) metaDataCopy.putAll(commitMetaData); metaDataCopy.put("server", server); _repository.removeTag(tag, metaDataCopy); return "undeployed"; } else return L.l("'{0}' failed to remove application '{1}'", controller, tag); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); return e.toString(); } } /** * @deprecated */ @Query public boolean sendAddFileQuery(long id, String to, String from, DeployAddFileQuery query) { String tag = query.getTag(); String name = query.getName(); String contentHash = query.getHex(); try { DeployControllerMXBean deploy = findController(tag); if (deploy == null) { if (log.isLoggable(Level.FINE)) log.fine(this + " sendAddFileQuery '" + tag + "' is an unknown DeployController"); getBroker().queryResult(id, from, to, "no-deploy: " + tag); return true; } Path root = Vfs.lookup(deploy.getRootDirectory()); root = root.createRoot(); Path path = root.lookup(name); if (! path.getParent().exists()) path.getParent().mkdirs(); _repository.expandToPath(contentHash, path); getBroker().queryResult(id, from, to, "ok"); return true; } catch (Exception e) { log.log(Level.FINE, e.toString(), e); getBroker().queryResult(id, from, to, "fail"); return true; } } /** * @deprecated **/ @Query public boolean listWebApps(long id, String to, String from, ListWebAppsQuery listQuery) { ArrayList<WebAppQuery> apps = new ArrayList<WebAppQuery>(); String stage = _server.getStage(); for (HostController host : _server.getHostControllers()) { if (listQuery.getHost().equals(host.getName())) { WebAppContainer webAppContainer = host.getDeployInstance().getWebAppContainer(); for (WebAppController webApp : webAppContainer.getWebAppList()) { WebAppQuery q = new WebAppQuery(); String name = webApp.getId(); if (name.startsWith("/")) name = name.substring(1); q.setTag("wars/" + stage + "/" + host.getName() + "/" + name); q.setHost(host.getName()); q.setUrl(webApp.getURL()); apps.add(q); } } } getBroker() .queryResult(id, from, to, apps.toArray(new WebAppQuery[apps.size()])); return true; } /** * @deprecated **/ @Query public boolean listTags(long id, String to, String from, ListTagsQuery listQuery) { ArrayList<TagQuery> tags = new ArrayList<TagQuery>(); for (String tag : _repository.getTagMap().keySet()) { if (tag.startsWith("wars/default") || tag.startsWith("ears/default")) { int p = "wars/default/".length(); int q = tag.indexOf('/', p + 1); if (q < 0) continue; String host = tag.substring(p, q); // String name = tag.substring(q + 1); tags.add(new TagQuery(host, tag)); } } getBroker() .queryResult(id, from, to, tags.toArray(new TagQuery[tags.size()])); return true; } /** * @deprecated **/ @Query public boolean listHosts(long id, String to, String from, ListHostsQuery query) { List<HostQuery> hosts = new ArrayList<HostQuery>(); for (HostController controller : _server.getHostControllers()) { if ("admin.resin".equals(controller.getName())) continue; HostQuery q = new HostQuery(); q.setName(controller.getName()); hosts.add(q); } getBroker() .queryResult(id, from, to, hosts.toArray(new HostQuery[hosts.size()])); return true; } /** * @deprecated **/ @Query public boolean status(long id, String to, String from, StatusQuery query) { String tag = query.getTag(); String errorMessage = statusMessage(tag); String state = null; StatusQuery result = new StatusQuery(tag, state, errorMessage); getBroker().queryResult(id, from, to, result); return true; } private String statusMessage(String tag) { int p = tag.indexOf('/'); int q = tag.indexOf('/', p + 1); int r = tag.lastIndexOf('/'); if (p < 0 || q < 0 || r < 0) return L.l("'{0}' is an unknown type", tag); String type = tag.substring(0, p); // String stage = tag.substring(p + 1, q); String host = tag.substring(q + 1, r); String name = tag.substring(r + 1); // String state = null; String errorMessage = tag + " is an unknown resource"; try { if (type.equals("ears")) { String pattern = "resin:type=EApp,Host=" + host + ",name=" + name; EAppMXBean ear = (EAppMXBean) Jmx.findGlobal(pattern); if (ear != null) { ear.update(); // state = ear.getState(); errorMessage = ear.getErrorMessage(); return errorMessage; } else return L.l("'{0}' is an unknown ear", tag); } else if (type.equals("wars")) { String pattern = "resin:type=WebApp,Host=" + host + ",name=/" + name; WebAppMXBean war = (WebAppMXBean) Jmx.findGlobal(pattern); if (war != null) { war.update(); // state = war.getState(); errorMessage = war.getErrorMessage(); return errorMessage; } else return L.l("'{0}' is an unknown war", tag); } else return L.l("'{0}' is an unknown tag", tag); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } return errorMessage; } private DeployControllerMXBean findController(String tag) { int p = tag.indexOf('/'); int q = tag.indexOf('/', p + 1); int r = tag.lastIndexOf('/'); if (p < 0 || q < 0 || r < 0) return null; String type = tag.substring(0, p); // String stage = tag.substring(p + 1, q); String host; String name; if (q < r) { host = tag.substring(q + 1, r); name = tag.substring(r + 1); } else { host = tag.substring(q + 1); name = ""; } try { if (type.equals("ears")) { String pattern = "resin:type=EApp,Host=" + host + ",name=" + name; EAppMXBean ear = (EAppMXBean) Jmx.findGlobal(pattern); return ear; } else if (type.equals("wars")) { String pattern = "resin:type=WebApp,Host=" + host + ",name=/" + name; WebAppMXBean war = (WebAppMXBean) Jmx.findGlobal(pattern); return war; } } catch (Exception e) { log.log(Level.FINEST, e.toString(), e); } return null; } }