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