blob: 3507f5324b273d6d9ebee65d7db72fbcee23ae2b [file] [log] [blame]
Stuart McCullochbb014372012-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 McCulloch39cc9ac2012-07-16 13:43:38 +00009import aQute.bnd.header.*;
10import aQute.bnd.osgi.*;
Stuart McCullochb215bfd2012-09-06 18:28:06 +000011import aQute.bnd.service.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000012import aQute.libg.command.*;
13import aQute.libg.generics.*;
Stuart McCullochbb014372012-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 McCullochbb014372012-06-07 21:57:32 +000022 */
23public abstract class ProjectLauncher {
Stuart McCulloch2286f232012-06-15 13:27:53 +000024 private final Project project;
25 private long timeout = 0;
Stuart McCullochbb014372012-06-07 21:57:32 +000026 private final List<String> classpath = new ArrayList<String>();
Stuart McCulloch2286f232012-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 McCullochbb014372012-06-07 21:57:32 +000036
Stuart McCulloch2286f232012-06-15 13:27:53 +000037 private boolean trace;
38 private boolean keep;
39 private int framework;
Stuart McCullochbb014372012-06-07 21:57:32 +000040
Stuart McCulloch2286f232012-06-15 13:27:53 +000041 public final static int SERVICES = 10111;
42 public final static int NONE = 20123;
Stuart McCullochbb014372012-06-07 21:57:32 +000043
44 // MUST BE ALIGNED WITH LAUNCHER
Stuart McCulloch2286f232012-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 McCullochbb014372012-06-07 21:57:32 +000055
Stuart McCulloch2286f232012-06-15 13:27:53 +000056 public final static String EMBEDDED_ACTIVATOR = "Embedded-Activator";
Stuart McCullochbb014372012-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
Stuart McCullochb215bfd2012-09-06 18:28:06 +0000100 List<Container> fws = project.getBundles(Strategy.HIGHEST, project.getProperty(Constants.RUNFW), Constants.RUNFW);
Stuart McCullochbb014372012-06-07 21:57:32 +0000101 runpath.addAll(fws);
102
103 for (Container c : runpath) {
104 addClasspath(c);
105 }
106
107 runvm.addAll(project.getRunVM());
108 runproperties = project.getRunProperties();
109
110 storageDir = project.getRunStorage();
111 if (storageDir == null) {
112 storageDir = new File(project.getTarget(), "fw");
113 }
114 }
115
116 private int getRunframework(String property) {
117 if (Constants.RUNFRAMEWORK_NONE.equalsIgnoreCase(property))
118 return NONE;
119 else if (Constants.RUNFRAMEWORK_SERVICES.equalsIgnoreCase(property))
120 return SERVICES;
121
122 return SERVICES;
123 }
124
125 public void addClasspath(Container container) throws Exception {
126 if (container.getError() != null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000127 project.error("Cannot launch because %s has reported %s", container.getProject(), container.getError());
Stuart McCullochbb014372012-06-07 21:57:32 +0000128 } else {
129 Collection<Container> members = container.getMembers();
130 for (Container m : members) {
131 String path = m.getFile().getAbsolutePath();
132 if (!classpath.contains(path)) {
133 classpath.add(path);
134
135 Manifest manifest = m.getManifest();
136
137 if (manifest != null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000138 Parameters exports = project.parseHeader(manifest.getMainAttributes().getValue(
139 Constants.EXPORT_PACKAGE));
140 for (Entry<String,Attrs> e : exports.entrySet()) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000141 if (!runsystempackages.containsKey(e.getKey()))
142 runsystempackages.put(e.getKey(), e.getValue());
143 }
144
145 // Allow activators on the runpath. They are called
146 // after
147 // the framework is completely initialized wit the
148 // system
149 // context.
Stuart McCulloch2286f232012-06-15 13:27:53 +0000150 String activator = manifest.getMainAttributes().getValue(EMBEDDED_ACTIVATOR);
Stuart McCullochbb014372012-06-07 21:57:32 +0000151 if (activator != null)
152 activators.add(activator);
153 }
154 }
155 }
156 }
157 }
158
159 public void addRunBundle(String f) {
160 runbundles.add(f);
161 }
162
163 public Collection<String> getRunBundles() {
164 return runbundles;
165 }
166
167 public void addRunVM(String arg) {
168 runvm.add(arg);
169 }
170
171 public List<String> getRunpath() {
172 return classpath;
173 }
174
175 public Collection<String> getClasspath() {
176 return classpath;
177 }
178
179 public Collection<String> getRunVM() {
180 return runvm;
181 }
182
183 public Collection<String> getArguments() {
184 return Collections.emptySet();
185 }
186
Stuart McCulloch2286f232012-06-15 13:27:53 +0000187 public Map<String,String> getRunProperties() {
Stuart McCullochbb014372012-06-07 21:57:32 +0000188 return runproperties;
189 }
190
191 public File getStorageDir() {
192 return storageDir;
193 }
194
195 public abstract String getMainTypeName();
196
197 public abstract void update() throws Exception;
198
199 public int launch() throws Exception {
200 prepare();
201 java = new Command();
202 java.add(project.getProperty("java", "java"));
203 java.add("-cp");
204 java.add(Processor.join(getClasspath(), File.pathSeparator));
205 java.addAll(getRunVM());
206 java.add(getMainTypeName());
207 java.addAll(getArguments());
208 if (timeout != 0)
209 java.setTimeout(timeout + 1000, TimeUnit.MILLISECONDS);
210
211 try {
Stuart McCullochffa8aaf2012-06-17 20:38:35 +0000212 int result = java.execute(System.in, System.err, System.err);
Stuart McCullochbb014372012-06-07 21:57:32 +0000213 if (result == Integer.MIN_VALUE)
214 return TIMEDOUT;
215 reportResult(result);
216 return result;
Stuart McCulloch2286f232012-06-15 13:27:53 +0000217 }
218 finally {
Stuart McCullochbb014372012-06-07 21:57:32 +0000219 cleanup();
220 }
221 }
222
223 /**
Stuart McCulloch2286f232012-06-15 13:27:53 +0000224 * Is called after the process exists. Can you be used to cleanup the
225 * properties file.
Stuart McCullochbb014372012-06-07 21:57:32 +0000226 */
Stuart McCulloch2286f232012-06-15 13:27:53 +0000227
Stuart McCullochbb014372012-06-07 21:57:32 +0000228 public void cleanup() {
229 // do nothing by default
230 }
231
232 protected void reportResult(int result) {
233 switch (result) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000234 case OK :
235 project.trace("Command terminated normal %s", java);
236 break;
237 case TIMEDOUT :
238 project.error("Launch timedout: %s", java);
239 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000240
Stuart McCulloch2286f232012-06-15 13:27:53 +0000241 case ERROR :
242 project.error("Launch errored: %s", java);
243 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000244
Stuart McCulloch2286f232012-06-15 13:27:53 +0000245 case WARNING :
246 project.warning("Launch had a warning %s", java);
247 break;
248 default :
249 project.error("Exit code remote process %d: %s", result, java);
250 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000251 }
252 }
253
254 public void setTimeout(long timeout, TimeUnit unit) {
255 this.timeout = unit.convert(timeout, TimeUnit.MILLISECONDS);
256 }
257
258 public long getTimeout() {
259 return this.timeout;
260 }
261
262 public void cancel() {
263 java.cancel();
264 }
265
Stuart McCulloch2286f232012-06-15 13:27:53 +0000266 public Map<String, ? extends Map<String,String>> getSystemPackages() {
Stuart McCullochbb014372012-06-07 21:57:32 +0000267 return runsystempackages.asMapMap();
268 }
269
270 public void setKeep(boolean keep) {
271 this.keep = keep;
272 }
273
274 public boolean isKeep() {
275 return keep;
276 }
277
278 public void setTrace(boolean level) {
279 this.trace = level;
280 }
281
282 public boolean getTrace() {
283 return this.trace;
284 }
285
286 /**
287 * Should be called when all the changes to the launchers are set. Will
288 * calculate whatever is necessary for the launcher.
289 *
290 * @throws Exception
291 */
292 public abstract void prepare() throws Exception;
293
294 public Project getProject() {
295 return project;
296 }
297
298 public boolean addActivator(String e) {
299 return activators.add(e);
300 }
301
302 public Collection<String> getActivators() {
303 return Collections.unmodifiableCollection(activators);
304 }
305
306 /**
307 * Either NONE or SERVICES to indicate how the remote end launches. NONE
308 * means it should not use the classpath to run a framework. This likely
309 * requires some dummy framework support. SERVICES means it should load the
310 * framework from the claspath.
311 *
312 * @return
313 */
314 public int getRunFramework() {
315 return framework;
316 }
317
318 public void setRunFramework(int n) {
319 assert n == NONE || n == SERVICES;
320 this.framework = n;
321 }
322
323 /**
324 * Add the specification for a set of bundles the runpath if it does not
325 * already is included. This can be used by subclasses to ensure the proper
326 * jars are on the classpath.
327 *
328 * @param defaultSpec
329 * The default spec for default jars
330 */
331 public void addDefault(String defaultSpec) throws Exception {
332 Collection<Container> deflts = project.getBundles(Strategy.HIGHEST, defaultSpec, null);
333 for (Container c : deflts)
334 addClasspath(c);
335 }
336
337 /**
338 * Create a self executable.
339 */
340
341 public Jar executable() throws Exception {
342 throw new UnsupportedOperationException();
343 }
344
345 public void clear() {
346 errors.clear();
347 warnings.clear();
348 }
349
350 public List<String> getErrors() {
351 return Collections.unmodifiableList(errors);
352 }
353
354 public List<String> getWarnings() {
355 return Collections.unmodifiableList(warnings);
356 }
357
358 protected void error(String message, Object... args) {
359 String formatted = String.format(message, args);
360 errors.add(formatted);
361 }
362
363 protected void warning(String message, Object... args) {
364 String formatted = String.format(message, args);
365 warnings.add(formatted);
366 }
367}