001/* 002 * (C) Copyright 2006-2017O 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 org.nuxeo.runtime.api.Framework; 022 023/** 024 * A dynamic component deployer which enable tests to deploy new contributions after the test was started (i.e. from 025 * inside the test method) The deployer is reloading all the components and reinject the test members. 026 * 027 * @author bogdan 028 */ 029public class HotDeployer { 030 031 public static final String DEPLOY_ACTION = "deploy"; 032 033 public static final String RESTART_ACTION = "restart"; 034 035 public static final String RESET_ACTION = "reset"; 036 037 protected FeaturesRunner runner; 038 039 protected RuntimeHarness harness; 040 041 protected ActionHandler head; 042 043 public HotDeployer(FeaturesRunner runner, RuntimeHarness harness) { 044 this.runner = runner; 045 this.harness = harness; 046 this.head = new DefaultDeployHandler(); 047 } 048 049 /** 050 * Add a custom deploy action that wraps the default action. You should call next.exec(...) in your action code to 051 * call the next action 052 */ 053 public HotDeployer addHandler(ActionHandler action) { 054 action.next = this.head; 055 this.head = action; 056 return this; 057 } 058 059 /** 060 * Remove the given handler if already registered 061 */ 062 public boolean removeHandler(ActionHandler handler) { 063 ActionHandler p = null; 064 ActionHandler h = this.head; 065 while (h != null && h != handler) { 066 p = h; 067 h = h.next; 068 } 069 if (h != null) { 070 if (p == null) { 071 this.head = h.next; 072 } else { 073 p.next = h.next; 074 } 075 h.next = null; 076 return true; 077 } 078 return false; 079 } 080 081 /** 082 * Deploy the given list of contributions. The format is [@]bundleId[:componentPath]. If no component path is 083 * specified then the bundle identified by the bundleId part will be deployed. If a componentPath is given 084 * {@link RuntimeHarness#deployContrib(String,String)} will be used to deploy the contribution. If 085 * bundleId:componentPath expression is prefixed by a '@' character then 086 * {@link RuntimeHarness#deployTestContrib(String,String)} will be used to deploy the contribution. 087 */ 088 public void deploy(String... contribs) throws Exception { 089 this.head.exec(DEPLOY_ACTION, contribs); 090 reinject(); 091 } 092 093 /** 094 * Restart the components and preserve the current registry state. 095 */ 096 public void restart() throws Exception { 097 this.head.exec(RESTART_ACTION); 098 reinject(); 099 } 100 101 /** 102 * Restart the components and revert to the initial registry snapshot if any. 103 */ 104 public void reset() throws Exception { 105 this.head.exec(RESET_ACTION); 106 reinject(); 107 } 108 109 public void reinject() { 110 runner.getInjector().injectMembers(runner.getTargetTestInstance()); 111 } 112 113 /** 114 * Deploy actions are usually used by features to customize the deployment of the runtime feature (which is using 115 * the DeaultDeployAction) 116 * 117 * @author bogdan 118 */ 119 public static abstract class ActionHandler { 120 121 protected ActionHandler next; 122 123 /** 124 * Can wrap another deploy action with custom code. Should call the next action using 125 * <code>next.exec(action, args...)</code> 126 */ 127 public abstract void exec(String action, String... args) throws Exception; 128 129 } 130 131 /** 132 * This action has no next action and will deploy the contributions and then reload the component manager 133 * 134 * @author bogdan 135 */ 136 protected class DefaultDeployHandler extends ActionHandler { 137 138 @Override 139 public void exec(String action, String... args) throws Exception { 140 if (DEPLOY_ACTION.equals(action)) { 141 deploy(args); 142 } else if (RESTART_ACTION.equals(action)) { 143 restart(); 144 } else if (RESET_ACTION.equals(action)) { 145 reset(); 146 } 147 } 148 149 public void deploy(String... contribs) throws Exception { 150 if (contribs != null && contribs.length > 0) { 151 for (String contrib : contribs) { 152 int i = contrib.indexOf(':'); 153 if (i > -1) { 154 String bundleId = contrib.substring(0, i); 155 if (bundleId.startsWith("@")) { 156 bundleId = bundleId.substring(1); 157 harness.deployTestContrib(bundleId, contrib.substring(i + 1)); 158 } else { 159 harness.deployContrib(bundleId, contrib.substring(i + 1)); 160 } 161 } else { 162 harness.deployBundle(contrib); 163 } 164 } 165 } 166 // use false to prevent removing the local test method deployments 167 Framework.getRuntime().getComponentManager().refresh(false); 168 } 169 170 public void restart() throws Exception { 171 Framework.getRuntime().getComponentManager().restart(false); 172 } 173 174 public void reset() throws Exception { 175 Framework.getRuntime().getComponentManager().restart(true); 176 } 177 178 } 179 180}