001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (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.lang.annotation.Annotation; 022import java.lang.reflect.Method; 023import java.net.URL; 024import java.util.Collection; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.Iterator; 028import java.util.LinkedList; 029import java.util.List; 030import java.util.Map; 031import java.util.Set; 032 033import javax.inject.Inject; 034 035import org.junit.rules.MethodRule; 036import org.junit.runners.model.FrameworkMethod; 037import org.junit.runners.model.Statement; 038 039import org.nuxeo.runtime.model.RuntimeContext; 040import org.nuxeo.runtime.osgi.OSGiRuntimeService; 041 042import org.osgi.framework.Bundle; 043 044import com.google.common.base.Supplier; 045import com.google.common.collect.Multimaps; 046import com.google.common.collect.SetMultimap; 047 048/** 049 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 050 */ 051public class RuntimeDeployment { 052 053 Set<String> bundles = new HashSet<>(); 054 055 Map<String, Collection<String>> mainContribs = new HashMap<>(); 056 057 SetMultimap<String, String> mainIndex = Multimaps.newSetMultimap(mainContribs, new Supplier<Set<String>>() { 058 @Override 059 public Set<String> get() { 060 return new HashSet<>(); 061 } 062 }); 063 064 Map<String, Collection<String>> localContribs = new HashMap<>(); 065 066 SetMultimap<String, String> localIndex = Multimaps.newSetMultimap(localContribs, new Supplier<Set<String>>() { 067 @Override 068 public Set<String> get() { 069 return new HashSet<>(); 070 } 071 }); 072 073 protected LinkedList<RuntimeContext> contexts = new LinkedList<>(); 074 075 protected void index(Class<?> clazz) { 076 AnnotationScanner scanner = FeaturesRunner.scanner; 077 scanner.scan(clazz); 078 List<? extends Annotation> annos = scanner.getAnnotations(clazz); 079 if (annos == null) { 080 return; 081 } 082 for (Annotation anno : annos) { 083 if (anno.annotationType() == Deploy.class) { 084 index((Deploy) anno); 085 } else if (anno.annotationType() == LocalDeploy.class) { 086 index((LocalDeploy) anno); 087 } 088 } 089 } 090 091 protected void index(RunnerFeature feature) { 092 index(feature.getClass()); 093 } 094 095 protected void index(Method method) { 096 index(method.getAnnotation(Deploy.class)); 097 index(method.getAnnotation(LocalDeploy.class)); 098 } 099 100 protected void index(Deploy config) { 101 if (config == null) { 102 return; 103 } 104 for (String each : config.value()) { 105 index(each, mainIndex); 106 } 107 } 108 109 protected void index(LocalDeploy config) { 110 if (config == null) { 111 return; 112 } 113 for (String each : config.value()) { 114 index(each, localIndex); 115 } 116 } 117 118 protected void index(Features features) { 119 for (Class<?> each : features.value()) { 120 index(each); 121 } 122 } 123 124 protected void index(String directive, SetMultimap<String, String> contribs) { 125 int sepIndex = directive.indexOf(':'); 126 if (sepIndex == -1) { 127 bundles.add(directive); 128 } else { 129 String bundle = directive.substring(0, sepIndex); 130 String resource = directive.substring(sepIndex + 1); 131 contribs.put(bundle, resource); 132 } 133 } 134 135 protected void deploy(FeaturesRunner runner, RuntimeHarness harness) { 136 AssertionError errors = new AssertionError("deployment errors"); 137 OSGiRuntimeService runtime = (OSGiRuntimeService) harness.getContext().getRuntime(); 138 for (String name : bundles) { 139 Bundle bundle = harness.getOSGiAdapter().getBundle(name); 140 if (bundle == null) { 141 try { 142 harness.deployBundle(name); 143 bundle = harness.getOSGiAdapter().getBundle(name); 144 if (bundle == null) { 145 throw new UnsupportedOperationException("Should not occur"); 146 } 147 } catch (Exception error) { 148 errors.addSuppressed(error); 149 continue; 150 } 151 contexts.add(runtime.getContext(bundle)); 152 } 153 try { 154 // deploy bundle contribs 155 for (String resource : mainIndex.removeAll(name)) { 156 try { 157 harness.deployContrib(name, resource); 158 } catch (Exception error) { 159 errors.addSuppressed(error); 160 } 161 } 162 // deploy local contribs 163 for (String resource : localIndex.removeAll(name)) { 164 URL url = runner.getTargetTestResource(resource); 165 if (url == null) { 166 url = bundle.getEntry(resource); 167 } 168 if (url == null) { 169 url = runner.getTargetTestClass().getClassLoader().getResource(resource); 170 } 171 if (url == null) { 172 throw new AssertionError("Cannot find " + resource + " in " + name); 173 } 174 contexts.add(harness.deployTestContrib(name, url)); 175 } 176 } catch (Exception error) { 177 errors.addSuppressed(error); 178 } 179 } 180 181 for (Map.Entry<String, String> resource : mainIndex.entries()) { 182 try { 183 harness.deployContrib(resource.getKey(), resource.getValue()); 184 } catch (Exception error) { 185 errors.addSuppressed(error); 186 } 187 } 188 for (Map.Entry<String, String> resource : localIndex.entries()) { 189 try { 190 contexts.add(harness.deployTestContrib(resource.getKey(), resource.getValue())); 191 } catch (Exception error) { 192 errors.addSuppressed(error); 193 } 194 } 195 196 if (errors.getSuppressed().length > 0) { 197 throw errors; 198 } 199 200 } 201 202 void undeploy() { 203 AssertionError errors = new AssertionError("deployment errors"); 204 205 Iterator<RuntimeContext> it = contexts.descendingIterator(); 206 while (it.hasNext()) { 207 RuntimeContext each = it.next(); 208 it.remove(); 209 try { 210 each.destroy(); 211 } catch (RuntimeException error) { 212 errors.addSuppressed(error); 213 } 214 } 215 216 if (errors.getSuppressed().length > 0) { 217 throw errors; 218 } 219 } 220 221 public static RuntimeDeployment onTest(FeaturesRunner runner) { 222 RuntimeDeployment deployment = new RuntimeDeployment(); 223 deployment.index(runner.getDescription().getTestClass()); 224 for (RunnerFeature each : runner.getFeatures()) { 225 deployment.index(each); 226 } 227 return deployment; 228 } 229 230 public static MethodRule onMethod() { 231 return new OnMethod(); 232 } 233 234 protected static class OnMethod implements MethodRule { 235 236 @Inject 237 protected FeaturesRunner runner; 238 239 @Override 240 public Statement apply(Statement base, FrameworkMethod method, Object target) { 241 RuntimeDeployment deployment = new RuntimeDeployment(); 242 deployment.index(method.getMethod()); 243 return deployment.onStatement(runner, runner.getFeature(RuntimeFeature.class).harness, base); 244 } 245 246 } 247 248 protected Statement onStatement(FeaturesRunner runner, RuntimeHarness harness, Statement base) { 249 return new DeploymentStatement(runner, harness, base); 250 } 251 252 protected class DeploymentStatement extends Statement { 253 254 protected final FeaturesRunner runner; 255 256 protected final RuntimeHarness harness; 257 258 protected final Statement base; 259 260 public DeploymentStatement(FeaturesRunner runner, RuntimeHarness harness, Statement base) { 261 this.runner = runner; 262 this.harness = harness; 263 this.base = base; 264 } 265 266 @Override 267 public void evaluate() throws Throwable { 268 deploy(runner, harness); 269 try { 270 base.evaluate(); 271 } finally { 272 undeploy(); 273 } 274 } 275 276 } 277 278}