Source code: org/eclipse/osgi/framework/internal/core/BundleNativeCode.java
1 /*******************************************************************************
2 * Copyright (c) 2003, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.osgi.framework.internal.core;
12
13 import java.util.Enumeration;
14 import java.util.Vector;
15 import org.eclipse.osgi.service.resolver.Version;
16 import org.eclipse.osgi.util.ManifestElement;
17 import org.osgi.framework.*;
18 import org.osgi.framework.Constants;
19 import org.osgi.framework.InvalidSyntaxException;
20
21 /**
22 * This class represents a description of native code.
23 *
24 * Native Code dependencies
25 *
26 * The Bundle-NativeCode header allows a bundle to carry the native code it
27 * needs, and make use of it when it is installed. The bundle must have
28 * RuntimePermission in order to run native code in the Framework. The value of
29 * the header must conform to the following syntax:
30 *
31 *
32 * Bundle-NativeCode: nativecode-clause ( , nativecode-clause)*
33 * nativecode-clause: nativepaths ( ; env-parameter )* nativepaths: nativepath ( ;
34 * nativepath )* env-parameter: ( processordef | osnamedef | osversiondef |
35 * languagedef ) processordef: <I>processor= </I>token osnamedef: <I>osname=
36 * </I>token osversiondef: <I>osversion= </I>token languagedef: <I>language=
37 * </I>token
38 *
39 * For example:
40 *
41 * Bundle-NativeCode: http.dll ; osname=Win95; processor=x86; language=en,
42 * libhttp.so; osname=Solaris; processor=sparc
43 *
44 * The Bundle-NativeCode header allows a bundle programmer to specify an
45 * environment, and to declare what native code libraries it carries for that
46 * specific environment. The environment is characterized by the following
47 * properties:
48 *
49 * <UL>
50 * <LI><CODE>processor</CODE> The processor on which the hosting the
51 * Framework is running. It is compared against <CODE>
52 * org.osgi.framework.processor</CODE>.
53 * <LI><CODE>osname</CODE> The operating system name. It is compared against
54 * <CODE>org.osgi.framework.os.name</CODE>.
55 * <LI><CODE>selection-filter</CODE> An optional filter that can be used to
56 * match against system properties. If the filter does not match then the
57 * native code clause will not be selected.
58 * <LI><CODE>osversion</CODE> The version of the operating system. It is
59 * compared against <CODE>org.osgi.framework.os.version</CODE>.
60 * <LI><CODE>language</CODE> The language. It is compared against <CODE>
61 * org.osgi.framework.language</CODE>.
62 * </UL>
63 *
64 * These properties should follow the conventions and values defined in the
65 * "Open Software Description" specification.
66 *
67 * The Framework uses the following algorithm to find the "best" matching
68 * native code clause:
69 *
70 * <ol>
71 * <li>Pick the clauses with a matching processor and operating system with
72 * the one the Framework runs on. If no clause matches both the required
73 * processor and operating system, the bundle installation/activation fails. If
74 * only one clause matches, it can be used, otherwise, remaining steps are
75 * executed.</li>
76 * <li>Pick the clauses that best match the operating system version. If they
77 * match each other exactly, that clause is considered the best match. If there
78 * is only one clause with an exact match, it can be used. If there are more
79 * than one clause that matches the property, these clauses will be picked to
80 * perform the next step. Operating system versions are taken to be backward
81 * compatible. If there is no exact match in the clauses, clauses with
82 * operating system versions lower than the value specified in
83 * org.osgi.framework.osversion will be picked. If there is only one clause
84 * which has a compatible operating system version, it can be used. Otherwise,
85 * all clauses with compatible operating system versions will go through the
86 * next step. If no clause has a matching or compatible operating system
87 * version, pick the clause that does not have operating system version
88 * specified. If that is not possible, the bundle installation fails.</li>
89 * <li>Pick the clause that best matches the language. If more than one clause
90 * remains at that point, then the Framework is free to pick amongst them
91 * randomly. If no clauses have the exact match with the value of the property,
92 * pick the clause that does not have language specified. If that is not
93 * possible, the bundle installation fails.</li>
94 * </ol>
95 *
96 */
97 public class BundleNativeCode {
98 /**
99 * The Native Code paths for the Native Code entry
100 */
101 private Attribute nativepaths;
102 /**
103 * The processor attribute for this Native Code entry
104 */
105 private Attribute processor;
106 /**
107 * The osname attribute for this Native Code entry
108 */
109 private Attribute osname;
110 /**
111 * The language attribute for this Native Code entry
112 */
113 private Attribute language;
114 /**
115 * The osversion attribute for this Native Code entry
116 */
117 private Attribute osversion;
118 /**
119 * The filter attribute for this Native Code entry
120 */
121 private String filterString;
122 /**
123 * The Framework for this BundleNativeCode
124 */
125 private AbstractBundle bundle;
126
127 /**
128 * The AliasMapper used to alias OS Names.
129 */
130 private static AliasMapper aliasMapper = Framework.aliasMapper;
131
132 /**
133 * Constructor for BundleNativeCode. It reads bundle native code data from
134 * the manifest file.
135 *
136 */
137 protected BundleNativeCode(ManifestElement element, AbstractBundle bundle) {
138 this.bundle = bundle;
139 String[] nativePaths = element.getValueComponents();
140 for (int i = 0; i < nativePaths.length; i++) {
141 addPath(nativePaths[i]);
142 }
143 setAttribute(element, Constants.BUNDLE_NATIVECODE_OSNAME);
144 setAttribute(element, Constants.BUNDLE_NATIVECODE_PROCESSOR);
145 setAttribute(element, Constants.BUNDLE_NATIVECODE_OSVERSION);
146 setAttribute(element, Constants.BUNDLE_NATIVECODE_LANGUAGE);
147 setAttribute(element, Constants.SELECTION_FILTER_ATTRIBUTE);
148 }
149
150 private void setAttribute(ManifestElement element, String attribute) {
151 String[] attrValues = element.getAttributes(attribute);
152 if (attrValues != null) {
153 for (int i = 0; i < attrValues.length; i++) {
154 addAttribute(attribute, attrValues[i]);
155 }
156 }
157 }
158
159 /**
160 * Returns the native code paths.
161 *
162 * @return Vector of String code paths.
163 */
164 public String[] getPaths() {
165 if (nativepaths == null) {
166 return null;
167 }
168 String[] paths = new String[nativepaths.size()];
169 nativepaths.toArray(paths);
170 return (paths);
171 }
172
173 /**
174 * addPath is used to add a new element to the list of native files.
175 *
176 * @param nativepath
177 * new native file
178 */
179 protected void addPath(String nativepath) {
180 if (nativepaths == null) {
181 nativepaths = new Attribute();
182 }
183 nativepaths.addElement(nativepath);
184 }
185
186 /**
187 * addAttribute is used to add the specification-version string to the
188 * package description. It is the only key supported at this time.
189 *
190 * @param key
191 * attribute key name
192 * @param value
193 * attribute value name
194 */
195 protected synchronized void addAttribute(String key, String value) {
196 if (key.equals(Constants.BUNDLE_NATIVECODE_PROCESSOR)) {
197 if (processor == null) {
198 processor = new Attribute();
199 }
200 processor.addElement(aliasMapper.aliasProcessor(value));
201 return;
202 }
203 if (key.equals(Constants.BUNDLE_NATIVECODE_OSNAME)) {
204 if (osname == null) {
205 osname = new Attribute();
206 }
207 osname.addElement(aliasMapper.aliasOSName(value));
208 return;
209 }
210 if (key.equals(Constants.BUNDLE_NATIVECODE_OSVERSION)) {
211 if (osversion == null) {
212 osversion = new Attribute();
213 }
214 osversion.addElement(new Version(value));
215 return;
216 }
217 if (key.equals(Constants.SELECTION_FILTER_ATTRIBUTE)) {
218 if (filterString == null) {
219 filterString = value;
220 }
221 return;
222 }
223 if (key.equals(Constants.BUNDLE_NATIVECODE_LANGUAGE)) {
224 if (language == null) {
225 language = new Attribute();
226 }
227 language.addElement(value.toLowerCase());
228 return;
229 }
230 }
231
232 /**
233 * Override toString. Return a String representation of this object
234 *
235 * @return String representation of the object
236 */
237 public String toString() {
238 int size = nativepaths.size();
239 StringBuffer sb = new StringBuffer(50 * size);
240 for (int i = 0; i < size; i++) {
241 if (i > 0) {
242 sb.append(';');
243 }
244 sb.append(nativepaths.elementAt(i).toString());
245 }
246 if (processor != null) {
247 size = processor.size();
248 for (int i = 0; i < size; i++) {
249 sb.append(';');
250 sb.append(Constants.BUNDLE_NATIVECODE_PROCESSOR);
251 sb.append('=');
252 sb.append(processor.elementAt(i).toString());
253 }
254 }
255 if (osname != null) {
256 size = osname.size();
257 for (int i = 0; i < size; i++) {
258 sb.append(';');
259 sb.append(Constants.BUNDLE_NATIVECODE_OSNAME);
260 sb.append('=');
261 sb.append(osname.elementAt(i).toString());
262 }
263 }
264 if (osversion != null) {
265 size = osversion.size();
266 for (int i = 0; i < size; i++) {
267 sb.append(';');
268 sb.append(Constants.BUNDLE_NATIVECODE_OSVERSION);
269 sb.append('=');
270 sb.append(osversion.elementAt(i).toString());
271 }
272 }
273 if (language != null) {
274 size = language.size();
275 for (int i = 0; i < size; i++) {
276 sb.append(';');
277 sb.append(Constants.BUNDLE_NATIVECODE_LANGUAGE);
278 sb.append('=');
279 sb.append(language.elementAt(i).toString());
280 }
281 }
282 return (sb.toString());
283 }
284
285 /**
286 * Return the match value for the given processor and os name. A higher
287 * value indicates a better match.
288 *
289 * @param processor
290 * processor name to match against.
291 * @param osname
292 * os name to match against.
293 * @return match value
294 */
295 public int matchProcessorOSNameFilter(String processor, String osname) {
296 if ((this.processor == null) || (this.osname == null)) {
297 return (0);
298 }
299 String otherProcessor = aliasMapper.aliasProcessor(processor);
300 String otherOSName = (String) aliasMapper.aliasOSName(osname);
301 if (this.processor.equals(otherProcessor) && this.osname.equals(otherOSName) && matchFilter()) {
302 return (1);
303 }
304 return (0);
305 }
306
307 /**
308 * Return the higest matching value for the given os version that is less
309 * than or equal to the given os version.
310 *
311 * @param version
312 * os version to match against.
313 * @return version or null if no match.
314 */
315 public Version matchOSVersion(Version version) {
316 if (this.osversion == null) {
317 return Version.emptyVersion;
318 }
319 Version result = null;
320 int size = this.osversion.size();
321 for (int i = 0; i < size; i++) {
322 Version ver = (Version) this.osversion.elementAt(i);
323 int compare = ver.compareTo(version);
324 if (compare == 0) /* versions are equal; best possible match */{
325 return ver;
326 }
327 if (compare < 0) /* requested version < current OS version */{
328 if ((result == null) || (ver.compareTo(result) > 0)) {
329 result = ver; /*
330 * remember the highest version less than
331 * osversion
332 */
333 }
334 }
335 }
336 return result;
337 }
338
339 /**
340 * Return the match value for the given language. A higher value indicates
341 * a better match.
342 *
343 * @param language
344 * language name to match against.
345 * @return match value
346 */
347 public int matchLanguage(String language) {
348 if (this.language == null) {
349 return (1);
350 }
351 if (this.language.equals(language.toLowerCase())) {
352 return (2);
353 }
354 return (0);
355 }
356
357 public boolean matchFilter() {
358 if (filterString == null) {
359 return true;
360 }
361 FilterImpl filter;
362 try {
363 filter = new FilterImpl(filterString);
364 } catch (InvalidSyntaxException e) {
365 BundleException be = new BundleException(Msg.formatter.getString("BUNDLE_NATIVECODE_INVALID_FILTER"), e); //$NON-NLS-1$
366 bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be);
367 return false;
368 }
369 return filter.match(System.getProperties());
370 }
371
372 /**
373 * Extension of Vector for attributes.
374 */
375 static class Attribute extends Vector {
376 /**
377 * Attribute constructor.
378 *
379 */
380 Attribute() {
381 super(10, 10);
382 }
383
384 /**
385 * Perform an "OR" operation on equals.
386 *
387 * @param obj
388 * Object to test against.
389 * @return true if at least one attribute is equal; false otherwise.
390 */
391 public synchronized boolean equals(Object obj) {
392 for (int i = 0; i < elementCount; i++) {
393 Object data = elementData[i];
394 if (data instanceof String) {
395 if (elementData[i].equals(obj)) {
396 return (true);
397 }
398 } else {
399 Enumeration e = ((Vector) data).elements();
400 while (e.hasMoreElements()) {
401 if (((String) e.nextElement()).equals(obj)) {
402 return true;
403 }
404 }
405 }
406 }
407 return (false);
408 }
409
410 /**
411 * Add the object if it is not already in the vector.
412 *
413 * @param obj
414 * Object to add to the vector.
415 */
416 public synchronized void addElement(Object obj) {
417 if (!contains(obj)) {
418 super.addElement(obj);
419 }
420 }
421 }
422 }