001/* 002 * (C) Copyright 2006-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 */ 019package org.nuxeo.runtime.test.runner; 020 021import java.security.InvalidParameterException; 022 023import org.nuxeo.runtime.api.Framework; 024import org.nuxeo.runtime.model.ComponentManager; 025 026/** 027 * A dynamic component deployer which enable tests to deploy new contributions after the test was started (i.e. from 028 * inside the test method) The deployer is reloading all the components and reinject the test members. 029 * 030 * @author bogdan 031 */ 032public class HotDeployer { 033 034 public static final String DEPLOY_ACTION = "deploy"; 035 036 protected static final String UNDEPLOY_ACTION = "undeploy"; 037 038 public static final String RESTART_ACTION = "restart"; 039 040 public static final String RESET_ACTION = "reset"; 041 042 public static final String RELOAD_ACTION = "reload"; 043 044 protected FeaturesRunner runner; 045 046 protected RuntimeHarness harness; 047 048 protected ActionHandler head; 049 050 public HotDeployer(FeaturesRunner runner, RuntimeHarness harness) { 051 this.runner = runner; 052 this.harness = harness; 053 head = new DefaultDeployHandler(); 054 } 055 056 /** 057 * Add a custom deploy action that wraps the default action. You should call next.exec(...) in your action code to 058 * call the next action 059 */ 060 public HotDeployer addHandler(ActionHandler action) { 061 action.next = head; 062 head = action; 063 return this; 064 } 065 066 /** 067 * Remove the given handler if already registered 068 */ 069 public boolean removeHandler(ActionHandler handler) { 070 ActionHandler p = null; 071 ActionHandler h = head; 072 while (h != null && h != handler) { 073 p = h; 074 h = h.next; 075 } 076 if (h != null) { 077 if (p == null) { 078 head = h.next; 079 } else { 080 p.next = h.next; 081 } 082 h.next = null; 083 return true; 084 } 085 return false; 086 } 087 088 /** 089 * Deploy the given list of contributions. The format is bundleId[:componentPath]. If no component path is 090 * specified then the bundle identified by the bundleId part will be deployed. If a componentPath is given 091 * {@link RuntimeHarness#deployContrib(String,String)} will be used to deploy the contribution. 092 */ 093 public void deploy(String... contribs) throws Exception { 094 head.exec(DEPLOY_ACTION, contribs); 095 reinject(); 096 } 097 098 public void undeploy(String... contribs) throws Exception { 099 head.exec(UNDEPLOY_ACTION, contribs); 100 reinject(); 101 } 102 103 /** 104 * Restart the components and preserve the current registry state. 105 */ 106 public void restart() throws Exception { 107 head.exec(RESTART_ACTION); 108 reinject(); 109 } 110 111 /** 112 * Restart the components and revert to the initial registry snapshot if any. 113 */ 114 public void reset() throws Exception { 115 head.exec(RESET_ACTION); 116 reinject(); 117 } 118 119 /** 120 * Reloads the components and preserve the current registry state. This action performs a standby/resume on 121 * {@link ComponentManager}. 122 * 123 * @since 10.2 124 */ 125 public void reload() throws Exception { 126 head.exec(RELOAD_ACTION); 127 reinject(); 128 } 129 130 public void reinject() { 131 runner.getInjector().injectMembers(runner.getTargetTestInstance()); 132 } 133 134 /** 135 * Deploy actions are usually used by features to customize the deployment of the runtime feature (which is using 136 * the DeaultDeployAction) 137 * 138 * @author bogdan 139 */ 140 public static abstract class ActionHandler { 141 142 protected ActionHandler next; 143 144 /** 145 * Can wrap another deploy action with custom code. Should call the next action using 146 * <code>next.exec(action, args...)</code> 147 */ 148 public abstract void exec(String action, String... args) throws Exception; 149 150 } 151 152 /** 153 * This action has no next action and will deploy the contributions and then reload the component manager 154 * 155 * @author bogdan 156 */ 157 protected class DefaultDeployHandler extends ActionHandler { 158 159 @Override 160 public void exec(String action, String... args) throws Exception { 161 if (DEPLOY_ACTION.equals(action)) { 162 deploy(args); 163 } else if (UNDEPLOY_ACTION.equals(action)) { 164 undeploy(args); 165 } else if (RESTART_ACTION.equals(action)) { 166 restart(); 167 } else if (RESET_ACTION.equals(action)) { 168 reset(); 169 } else if (RELOAD_ACTION.equals(action)) { 170 reload(); 171 } 172 } 173 174 public void undeploy(String... contribs) throws Exception { 175 if (contribs != null && contribs.length > 0) { 176 for (String contrib : contribs) { 177 int i = contrib.indexOf(':'); 178 if (i == -1) { 179 throw new InvalidParameterException("Cannot undeploy bundle " + contrib); 180 } 181 harness.undeployContrib(contrib.substring(0, i), contrib.substring(i + 1)); 182 } 183 } 184 // use false to prevent removing the local test method deployments 185 Framework.getRuntime().getComponentManager().refresh(false); 186 } 187 188 public void deploy(String... contribs) throws Exception { 189 if (contribs != null && contribs.length > 0) { 190 for (String contrib : contribs) { 191 int i = contrib.indexOf(':'); 192 if (i > -1) { 193 String bundleId = contrib.substring(0, i); 194 if (bundleId.startsWith("@")) { 195 bundleId = bundleId.substring(1); 196 harness.deployTestContrib(bundleId, contrib.substring(i + 1)); 197 } else { 198 harness.deployContrib(bundleId, contrib.substring(i + 1)); 199 } 200 } else { 201 harness.deployBundle(contrib); 202 } 203 } 204 } 205 // use false to prevent removing the local test method deployments 206 Framework.getRuntime().getComponentManager().refresh(false); 207 } 208 209 public void restart() { 210 Framework.getRuntime().getComponentManager().restart(false); 211 } 212 213 public void reset() { 214 Framework.getRuntime().getComponentManager().restart(true); 215 } 216 217 public void reload() { 218 ComponentManager componentManager = Framework.getRuntime().getComponentManager(); 219 componentManager.standby(); 220 componentManager.resume(); 221 } 222 223 } 224 225}