blob: 7c85fb3aa8789282fa6bf3ebee40677e8dede34b [file] [log] [blame]
Stuart McCullochf3173222012-06-07 21:57:32 +00001package aQute.bnd.build;
2
3import java.io.*;
4import java.util.*;
5import java.util.Map.Entry;
6import java.util.concurrent.*;
7import java.util.jar.*;
8
Stuart McCulloch42151ee2012-07-16 13:43:38 +00009import aQute.bnd.header.*;
10import aQute.bnd.osgi.*;
Stuart McCullochf3173222012-06-07 21:57:32 +000011import aQute.bnd.service.RepositoryPlugin.Strategy;
Stuart McCullochf3173222012-06-07 21:57:32 +000012import aQute.libg.command.*;
13import aQute.libg.generics.*;
Stuart McCullochf3173222012-06-07 21:57:32 +000014
15/**
16 * A Project Launcher is a base class to be extended by launchers. Launchers are
17 * JARs that launch a framework and install a number of bundles and then run the
18 * framework. A launcher jar must specify a Launcher-Class manifest header. This
19 * class is instantiated and cast to a LauncherPlugin. This plug in is then
20 * asked to provide a ProjectLauncher. This project launcher is then used by the
21 * project to run the code. Launchers must extend this class.
Stuart McCullochf3173222012-06-07 21:57:32 +000022 */
23public abstract class ProjectLauncher {
Stuart McCulloch4482c702012-06-15 13:27:53 +000024 private final Project project;
25 private long timeout = 0;
Stuart McCullochf3173222012-06-07 21:57:32 +000026 private final List<String> classpath = new ArrayList<String>();
Stuart McCulloch4482c702012-06-15 13:27:53 +000027 private List<String> runbundles = Create.list();
28 private final List<String> runvm = new ArrayList<String>();
29 private Map<String,String> runproperties;
30 private Command java;
31 private Parameters runsystempackages;
32 private final List<String> activators = Create.list();
33 private File storageDir;
34 private final List<String> warnings = Create.list();
35 private final List<String> errors = Create.list();
Stuart McCullochf3173222012-06-07 21:57:32 +000036
Stuart McCulloch4482c702012-06-15 13:27:53 +000037 private boolean trace;
38 private boolean keep;
39 private int framework;
Stuart McCullochf3173222012-06-07 21:57:32 +000040
Stuart McCulloch4482c702012-06-15 13:27:53 +000041 public final static int SERVICES = 10111;
42 public final static int NONE = 20123;
Stuart McCullochf3173222012-06-07 21:57:32 +000043
44 // MUST BE ALIGNED WITH LAUNCHER
Stuart McCulloch4482c702012-06-15 13:27:53 +000045 public final static int OK = 0;
46 public final static int WARNING = -1;
47 public final static int ERROR = -2;
48 public final static int TIMEDOUT = -3;
49 public final static int UPDATE_NEEDED = -4;
50 public final static int CANCELED = -5;
51 public final static int DUPLICATE_BUNDLE = -6;
52 public final static int RESOLVE_ERROR = -7;
53 public final static int ACTIVATOR_ERROR = -8;
54 public final static int CUSTOM_LAUNCHER = -128;
Stuart McCullochf3173222012-06-07 21:57:32 +000055
Stuart McCulloch4482c702012-06-15 13:27:53 +000056 public final static String EMBEDDED_ACTIVATOR = "Embedded-Activator";
Stuart McCullochf3173222012-06-07 21:57:32 +000057
58 public ProjectLauncher(Project project) throws Exception {
59 this.project = project;
60
61 updateFromProject();
62 }
63
64 /**
65 * Collect all the aspect from the project and set the local fields from
66 * them. Should be called
67 *
68 * @throws Exception
69 */
70 protected void updateFromProject() throws Exception {
71 // pkr: could not use this because this is killing the runtests.
72 // project.refresh();
73 runbundles.clear();
74 Collection<Container> run = project.getRunbundles();
75
76 for (Container container : run) {
77 File file = container.getFile();
78 if (file != null && (file.isFile() || file.isDirectory())) {
79 runbundles.add(file.getAbsolutePath());
80 } else {
81 warning("Bundle file \"%s\" does not exist", file);
82 }
83 }
84
85 if (project.getRunBuilds()) {
86 File[] builds = project.build();
87 if (builds != null)
88 for (File file : builds)
89 runbundles.add(file.getAbsolutePath());
90 }
91
92 Collection<Container> runpath = project.getRunpath();
93 runsystempackages = project.getParameters(Constants.RUNSYSTEMPACKAGES);
94 framework = getRunframework(project.getProperty(Constants.RUNFRAMEWORK));
95 trace = Processor.isTrue(project.getProperty(Constants.RUNTRACE));
96
97 timeout = Processor.getDuration(project.getProperty(Constants.RUNTIMEOUT), 0);
98 trace = Processor.isTrue(project.getProperty(Constants.RUNTRACE));
99
100 // For backward compatibility with bndtools launcher
Stuart McCulloch4482c702012-06-15 13:27:53 +0000101 List<Container> fws = project.getBundles(Strategy.HIGHEST, project.getProperty("-runfw"), "-runfw");
Stuart McCullochf3173222012-06-07 21:57:32 +0000102 runpath.addAll(fws);
103
104 for (Container c : runpath) {
105 addClasspath(c);
106 }
107
108 runvm.addAll(project.getRunVM());
109 runproperties = project.getRunProperties();
110
111 storageDir = project.getRunStorage();
112 if (storageDir == null) {
113 storageDir = new File(project.getTarget(), "fw");
114 }
115 }
116
117 private int getRunframework(String property) {
118 if (Constants.RUNFRAMEWORK_NONE.equalsIgnoreCase(property))
119 return NONE;
120 else if (Constants.RUNFRAMEWORK_SERVICES.equalsIgnoreCase(property))
121 return SERVICES;
122
123 return SERVICES;
124 }
125
126 public void addClasspath(Container container) throws Exception {
127 if (container.getError() != null) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000128 project.error("Cannot launch because %s has reported %s", container.getProject(), container.getError());
Stuart McCullochf3173222012-06-07 21:57:32 +0000129 } else {
130 Collection<Container> members = container.getMembers();
131 for (Container m : members) {
132 String path = m.getFile().getAbsolutePath();
133 if (!classpath.contains(path)) {
134 classpath.add(path);
135
136 Manifest manifest = m.getManifest();
137
138 if (manifest != null) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000139 Parameters exports = project.parseHeader(manifest.getMainAttributes().getValue(
140 Constants.EXPORT_PACKAGE));
141 for (Entry<String,Attrs> e : exports.entrySet()) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000142 if (!runsystempackages.containsKey(e.getKey()))
143 runsystempackages.put(e.getKey(), e.getValue());
144 }
145
146 // Allow activators on the runpath. They are called
147 // after
148 // the framework is completely initialized wit the
149 // system
150 // context.
Stuart McCulloch4482c702012-06-15 13:27:53 +0000151 String activator = manifest.getMainAttributes().getValue(EMBEDDED_ACTIVATOR);
Stuart McCullochf3173222012-06-07 21:57:32 +0000152 if (activator != null)
153 activators.add(activator);
154 }
155 }
156 }
157 }
158 }
159
160 public void addRunBundle(String f) {
161 runbundles.add(f);
162 }
163
164 public Collection<String> getRunBundles() {
165 return runbundles;
166 }
167
168 public void addRunVM(String arg) {
169 runvm.add(arg);
170 }
171
172 public List<String> getRunpath() {
173 return classpath;
174 }
175
176 public Collection<String> getClasspath() {
177 return classpath;
178 }
179
180 public Collection<String> getRunVM() {
181 return runvm;
182 }
183
184 public Collection<String> getArguments() {
185 return Collections.emptySet();
186 }
187
Stuart McCulloch4482c702012-06-15 13:27:53 +0000188 public Map<String,String> getRunProperties() {
Stuart McCullochf3173222012-06-07 21:57:32 +0000189 return runproperties;
190 }
191
192 public File getStorageDir() {
193 return storageDir;
194 }
195
196 public abstract String getMainTypeName();
197
198 public abstract void update() throws Exception;
199
200 public int launch() throws Exception {
201 prepare();
202 java = new Command();
203 java.add(project.getProperty("java", "java"));
204 java.add("-cp");
205 java.add(Processor.join(getClasspath(), File.pathSeparator));
206 java.addAll(getRunVM());
207 java.add(getMainTypeName());
208 java.addAll(getArguments());
209 if (timeout != 0)
210 java.setTimeout(timeout + 1000, TimeUnit.MILLISECONDS);
211
212 try {
Stuart McCulloch2b3253e2012-06-17 20:38:35 +0000213 int result = java.execute(System.in, System.err, System.err);
Stuart McCullochf3173222012-06-07 21:57:32 +0000214 if (result == Integer.MIN_VALUE)
215 return TIMEDOUT;
216 reportResult(result);
217 return result;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000218 }
219 finally {
Stuart McCullochf3173222012-06-07 21:57:32 +0000220 cleanup();
221 }
222 }
223
224 /**
Stuart McCulloch4482c702012-06-15 13:27:53 +0000225 * Is called after the process exists. Can you be used to cleanup the
226 * properties file.
Stuart McCullochf3173222012-06-07 21:57:32 +0000227 */
Stuart McCulloch4482c702012-06-15 13:27:53 +0000228
Stuart McCullochf3173222012-06-07 21:57:32 +0000229 public void cleanup() {
230 // do nothing by default
231 }
232
233 protected void reportResult(int result) {
234 switch (result) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000235 case OK :
236 project.trace("Command terminated normal %s", java);
237 break;
238 case TIMEDOUT :
239 project.error("Launch timedout: %s", java);
240 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000241
Stuart McCulloch4482c702012-06-15 13:27:53 +0000242 case ERROR :
243 project.error("Launch errored: %s", java);
244 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000245
Stuart McCulloch4482c702012-06-15 13:27:53 +0000246 case WARNING :
247 project.warning("Launch had a warning %s", java);
248 break;
249 default :
250 project.error("Exit code remote process %d: %s", result, java);
251 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000252 }
253 }
254
255 public void setTimeout(long timeout, TimeUnit unit) {
256 this.timeout = unit.convert(timeout, TimeUnit.MILLISECONDS);
257 }
258
259 public long getTimeout() {
260 return this.timeout;
261 }
262
263 public void cancel() {
264 java.cancel();
265 }
266
Stuart McCulloch4482c702012-06-15 13:27:53 +0000267 public Map<String, ? extends Map<String,String>> getSystemPackages() {
Stuart McCullochf3173222012-06-07 21:57:32 +0000268 return runsystempackages.asMapMap();
269 }
270
271 public void setKeep(boolean keep) {
272 this.keep = keep;
273 }
274
275 public boolean isKeep() {
276 return keep;
277 }
278
279 public void setTrace(boolean level) {
280 this.trace = level;
281 }
282
283 public boolean getTrace() {
284 return this.trace;
285 }
286
287 /**
288 * Should be called when all the changes to the launchers are set. Will
289 * calculate whatever is necessary for the launcher.
290 *
291 * @throws Exception
292 */
293 public abstract void prepare() throws Exception;
294
295 public Project getProject() {
296 return project;
297 }
298
299 public boolean addActivator(String e) {
300 return activators.add(e);
301 }
302
303 public Collection<String> getActivators() {
304 return Collections.unmodifiableCollection(activators);
305 }
306
307 /**
308 * Either NONE or SERVICES to indicate how the remote end launches. NONE
309 * means it should not use the classpath to run a framework. This likely
310 * requires some dummy framework support. SERVICES means it should load the
311 * framework from the claspath.
312 *
313 * @return
314 */
315 public int getRunFramework() {
316 return framework;
317 }
318
319 public void setRunFramework(int n) {
320 assert n == NONE || n == SERVICES;
321 this.framework = n;
322 }
323
324 /**
325 * Add the specification for a set of bundles the runpath if it does not
326 * already is included. This can be used by subclasses to ensure the proper
327 * jars are on the classpath.
328 *
329 * @param defaultSpec
330 * The default spec for default jars
331 */
332 public void addDefault(String defaultSpec) throws Exception {
333 Collection<Container> deflts = project.getBundles(Strategy.HIGHEST, defaultSpec, null);
334 for (Container c : deflts)
335 addClasspath(c);
336 }
337
338 /**
339 * Create a self executable.
340 */
341
342 public Jar executable() throws Exception {
343 throw new UnsupportedOperationException();
344 }
345
346 public void clear() {
347 errors.clear();
348 warnings.clear();
349 }
350
351 public List<String> getErrors() {
352 return Collections.unmodifiableList(errors);
353 }
354
355 public List<String> getWarnings() {
356 return Collections.unmodifiableList(warnings);
357 }
358
359 protected void error(String message, Object... args) {
360 String formatted = String.format(message, args);
361 errors.add(formatted);
362 }
363
364 protected void warning(String message, Object... args) {
365 String formatted = String.format(message, args);
366 warnings.add(formatted);
367 }
368}