blob: 2b789435a2c3fec8351cc5eeb6559f893bdffdf1 [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001package aQute.bnd.build;
2
3import java.io.*;
4import java.util.*;
5import java.util.concurrent.*;
6import java.util.jar.*;
7
8import aQute.bnd.service.RepositoryPlugin.Strategy;
9import aQute.lib.osgi.*;
10import aQute.libg.command.*;
11import aQute.libg.generics.*;
12
13/**
14 * A Project Launcher is a base class to be extended by launchers. Launchers are
15 * JARs that launch a framework and install a number of bundles and then run the
16 * framework. A launcher jar must specify a Launcher-Class manifest header. This
17 * class is instantiated and cast to a LauncherPlugin. This plug in is then
18 * asked to provide a ProjectLauncher. This project launcher is then used by the
19 * project to run the code. Launchers must extend this class.
20 *
21 */
22public abstract class ProjectLauncher {
23 private final Project project;
24 private long timeout = 0;
25 private final Collection<String> classpath = new ArrayList<String>();
26 private List<String> runbundles = Create.list();
27 private final List<String> runvm = new ArrayList<String>();
28 private Map<String, String> runproperties;
29 private Command java;
30 private Map<String, Map<String, String>> runsystempackages;
31 private final List<String> activators = Create.list();
32 private File storageDir;
33
34 private boolean trace;
35 private boolean keep;
36 private int framework;
37
38 public final static int SERVICES = 10111;
39 public final static int NONE = 20123;
40
41 // MUST BE ALIGNED WITH LAUNCHER
42 public final static int OK = 0;
43 public final static int WARNING = -1;
44 public final static int ERROR = -2;
45 public final static int TIMEDOUT = -3;
46 public final static int UPDATE_NEEDED = -4;
47 public final static int CANCELED = -5;
48 public final static int DUPLICATE_BUNDLE = -6;
49 public final static int RESOLVE_ERROR = -7;
50 public final static int ACTIVATOR_ERROR = -8;
51 public final static int CUSTOM_LAUNCHER = -128;
52
53 public final static String EMBEDDED_ACTIVATOR = "Embedded-Activator";
54
55 public ProjectLauncher(Project project) throws Exception {
56 this.project = project;
57
58 updateFromProject();
59 }
60
61 /**
62 * Collect all the aspect from the project and set the local fields from
63 * them. Should be called
64 *
65 * @throws Exception
66 */
67 protected void updateFromProject() throws Exception {
68 // pkr: could not use this because this is killing the runtests.
69 // project.refresh();
70 runbundles.clear();
71 Collection<Container> run = project.getRunbundles();
72
73 for (Container container : run) {
74 File file = container.getFile();
75 if (file != null && (file.isFile() || file.isDirectory()))
76 runbundles.add(file.getAbsolutePath());
77 }
78
79 if (project.getRunBuilds()) {
80 File[] builds = project.build();
81 if (builds != null)
82 for (File file : builds)
83 runbundles.add(file.getAbsolutePath());
84 }
85
86 Collection<Container> runpath = project.getRunpath();
87 runsystempackages = project.parseHeader(project.getProperty(Constants.RUNSYSTEMPACKAGES));
88 framework = getRunframework(project.getProperty(Constants.RUNFRAMEWORK));
89 trace = Processor.isTrue(project.getProperty(Constants.RUNTRACE));
90
91 timeout = Processor.getDuration(project.getProperty(Constants.RUNTIMEOUT), 0);
92 trace = Processor.isTrue(project.getProperty(Constants.RUNTRACE));
93
94 // For backward compatibility with bndtools launcher
95 List<Container> fws = project.getBundles(Strategy.HIGHEST, project.getProperty("-runfw"), "-runfw");
96 runpath.addAll(fws);
97
98 for (Container c : runpath) {
99 addClasspath(c);
100 }
101
102 runvm.addAll(project.getRunVM());
103 runproperties = project.getRunProperties();
104
105 storageDir = project.getRunStorage();
106 if (storageDir == null) {
107 storageDir = new File(project.getTarget(), "fw");
108 }
109 }
110
111 private int getRunframework(String property) {
112 if (Constants.RUNFRAMEWORK_NONE.equalsIgnoreCase(property))
113 return NONE;
114 else if (Constants.RUNFRAMEWORK_SERVICES.equalsIgnoreCase(property))
115 return SERVICES;
116
117 return SERVICES;
118 }
119
120 public void addClasspath(Container container) throws Exception {
121 if (container.getError() != null) {
122 project.error("Cannot launch because %s has reported %s", container.getProject(),
123 container.getError());
124 } else {
125 Collection<Container> members = container.getMembers();
126 for (Container m : members) {
127 String path = m.getFile().getAbsolutePath();
128 if (!classpath.contains(path)) {
129 classpath.add(path);
130
131 Manifest manifest = m.getManifest();
132 if (manifest != null) {
133 Map<String, Map<String, String>> exports = project.parseHeader(manifest
134 .getMainAttributes().getValue(Constants.EXPORT_PACKAGE));
135 for (Map.Entry<String, Map<String, String>> e : exports.entrySet()) {
136 if (!runsystempackages.containsKey(e.getKey()))
137 runsystempackages.put(e.getKey(), e.getValue());
138 }
139
140 // Allow activators on the runpath. They are called
141 // after
142 // the framework is completely initialized wit the
143 // system
144 // context.
145 String activator = manifest.getMainAttributes()
146 .getValue(EMBEDDED_ACTIVATOR);
147 if (activator != null)
148 activators.add(activator);
149 }
150 }
151 }
152 }
153 }
154
155 public void addRunBundle(String f) {
156 runbundles.add(f);
157 }
158
159 public Collection<String> getRunBundles() {
160 return runbundles;
161 }
162
163 public void addRunVM(String arg) {
164 runvm.add(arg);
165 }
166
167 public Collection<String> getRunpath() {
168 return classpath;
169 }
170
171 public Collection<String> getClasspath() {
172 return classpath;
173 }
174
175 public Collection<String> getRunVM() {
176 return runvm;
177 }
178
179 public Collection<String> getArguments() {
180 return Collections.emptySet();
181 }
182
183 public Map<String, String> getRunProperties() {
184 return runproperties;
185 }
186
187 public File getStorageDir() {
188 return storageDir;
189 }
190
191 public abstract String getMainTypeName();
192
193 public abstract void update() throws Exception;
194
195 public int launch() throws Exception {
196 prepare();
197 java = new Command();
198 java.add(project.getProperty("java", "java"));
199 java.add("-cp");
200 java.add(Processor.join(getClasspath(), File.pathSeparator));
201 java.addAll(getRunVM());
202 java.add(getMainTypeName());
203 java.addAll(getArguments());
204 if (timeout != 0)
205 java.setTimeout(timeout + 1000, TimeUnit.MILLISECONDS);
206
207 int result = java.execute(System.in, System.out, System.err);
208 if (result == Integer.MIN_VALUE)
209 return TIMEDOUT;
210
211 reportResult(result);
212 return result;
213 }
214
215 protected void reportResult(int result) {
216 switch (result) {
217 case OK:
218 project.trace("Command terminated normal %s", java);
219 break;
220 case TIMEDOUT:
221 project.error("Launch timedout: %s", java);
222 break;
223
224 case ERROR:
225 project.error("Launch errored: %s", java);
226 break;
227
228 case WARNING:
229 project.warning("Launch had a warning %s", java);
230 break;
231 default:
232 project.warning("Unknown code %d from launcher: %s", result, java);
233 break;
234 }
235 }
236
237 public void setTimeout(long timeout, TimeUnit unit) {
238 this.timeout = unit.convert(timeout, TimeUnit.MILLISECONDS);
239 }
240
241 public long getTimeout() {
242 return this.timeout;
243 }
244
245 public void cancel() {
246 java.cancel();
247 }
248
249 public Map<String, Map<String, String>> getSystemPackages() {
250 return runsystempackages;
251 }
252
253 public void setKeep(boolean keep) {
254 this.keep = keep;
255 }
256
257 public boolean isKeep() {
258 return keep;
259 }
260
261 public void setTrace(boolean level) {
262 this.trace = level;
263 }
264
265 public boolean getTrace() {
266 return this.trace;
267 }
268
269 /**
270 * Should be called when all the changes to the launchers are set. Will
271 * calculate whatever is necessary for the launcher.
272 *
273 * @throws Exception
274 */
275 public abstract void prepare() throws Exception;
276
277 public Project getProject() {
278 return project;
279 }
280
281 public boolean addActivator(String e) {
282 return activators.add(e);
283 }
284
285 public Collection<String> getActivators() {
286 return Collections.unmodifiableCollection(activators);
287 }
288
289 /**
290 * Either NONE or SERVICES to indicate how the remote end launches. NONE
291 * means it should not use the classpath to run a framework. This likely
292 * requires some dummy framework support. SERVICES means it should load the
293 * framework from the claspath.
294 *
295 * @return
296 */
297 public int getRunFramework() {
298 return framework;
299 }
300
301 public void setRunFramework(int n) {
302 assert n == NONE || n == SERVICES;
303 this.framework = n;
304 }
305
306 /**
307 * Add the specification for a set of bundles the runpath if it does not
308 * already is included. This can be used by subclasses to ensure the proper
309 * jars are on the classpath.
310 *
311 * @param defaultSpec
312 * The default spec for default jars
313 */
314 public void addDefault(String defaultSpec) throws Exception {
315 Collection<Container> deflts = project.getBundles(Strategy.HIGHEST, defaultSpec, null);
316 for (Container c : deflts)
317 addClasspath(c);
318 }
319
320 /**
321 * Create a self executable.
322 */
323
324 public Jar executable() throws Exception {
325 throw new UnsupportedOperationException();
326 }
327}