001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.felix.framework.util;
020
021 import java.io.*;
022
023 import java.util.ArrayList;
024 import java.util.HashMap;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Properties;
028
029 import org.apache.felix.framework.util.manifestparser.Capability;
030 import org.apache.felix.moduleloader.*;
031 import org.osgi.framework.Bundle;
032 import org.osgi.framework.Constants;
033 import org.osgi.framework.ServiceReference;
034
035 public class Util
036 {
037 /**
038 * Converts a module identifier to a bundle identifier. Module IDs
039 * are typically <tt><bundle-id>.<revision></tt>; this
040 * method returns only the portion corresponding to the bundle ID.
041 **/
042 public static long getBundleIdFromModuleId(String id)
043 {
044 try
045 {
046 String bundleId = (id.indexOf('.') >= 0)
047 ? id.substring(0, id.indexOf('.')) : id;
048 return Long.parseLong(bundleId);
049 }
050 catch (NumberFormatException ex)
051 {
052 return -1;
053 }
054 }
055
056 /**
057 * Converts a module identifier to a bundle identifier. Module IDs
058 * are typically <tt><bundle-id>.<revision></tt>; this
059 * method returns only the portion corresponding to the revision.
060 **/
061 public static int getModuleRevisionFromModuleId(String id)
062 {
063 try
064 {
065 int index = id.indexOf('.');
066 if (index >= 0)
067 {
068 return Integer.parseInt(id.substring(index + 1));
069 }
070 }
071 catch (NumberFormatException ex)
072 {
073 }
074 return -1;
075 }
076
077 public static String getClassName(String className)
078 {
079 if (className == null)
080 {
081 className = "";
082 }
083 return (className.lastIndexOf('.') < 0)
084 ? "" : className.substring(className.lastIndexOf('.') + 1);
085 }
086
087 public static String getClassPackage(String className)
088 {
089 if (className == null)
090 {
091 className = "";
092 }
093 return (className.lastIndexOf('.') < 0)
094 ? "" : className.substring(0, className.lastIndexOf('.'));
095 }
096
097 public static String getResourcePackage(String resource)
098 {
099 if (resource == null)
100 {
101 resource = "";
102 }
103 // NOTE: The package of a resource is tricky to determine since
104 // resources do not follow the same naming conventions as classes.
105 // This code is pessimistic and assumes that the package of a
106 // resource is everything up to the last '/' character. By making
107 // this choice, it will not be possible to load resources from
108 // imports using relative resource names. For example, if a
109 // bundle exports "foo" and an importer of "foo" tries to load
110 // "/foo/bar/myresource.txt", this will not be found in the exporter
111 // because the following algorithm assumes the package name is
112 // "foo.bar", not just "foo". This only affects imported resources,
113 // local resources will work as expected.
114 String pkgName = (resource.startsWith("/")) ? resource.substring(1) : resource;
115 pkgName = (pkgName.lastIndexOf('/') < 0)
116 ? "" : pkgName.substring(0, pkgName.lastIndexOf('/'));
117 pkgName = pkgName.replace('/', '.');
118 return pkgName;
119 }
120
121 /**
122 * <p>
123 * This is a simple utility class that attempts to load the named
124 * class using the class loader of the supplied class or
125 * the class loader of one of its super classes or their implemented
126 * interfaces. This is necessary during service registration to test
127 * whether a given service object implements its declared service
128 * interfaces.
129 * </p>
130 * <p>
131 * To perform this test, the framework must try to load
132 * the classes associated with the declared service interfaces, so
133 * it must choose a class loader. The class loader of the registering
134 * bundle cannot be used, since this disallows third parties to
135 * register service on behalf of another bundle. Consequently, the
136 * class loader of the service object must be used. However, this is
137 * also not sufficient since the class loader of the service object
138 * may not have direct access to the class in question.
139 * </p>
140 * <p>
141 * The service object's class loader may not have direct access to
142 * its service interface if it extends a super class from another
143 * bundle which implements the service interface from an imported
144 * bundle or if it implements an extension of the service interface
145 * from another bundle which imports the base interface from another
146 * bundle. In these cases, the service object's class loader only has
147 * access to the super class's class or the extended service interface,
148 * respectively, but not to the actual service interface.
149 * </p>
150 * <p>
151 * Thus, it is necessary to not only try to load the service interface
152 * class from the service object's class loader, but from the class
153 * loaders of any interfaces it implements and the class loaders of
154 * all super classes.
155 * </p>
156 * @param svcObj the class that is the root of the search.
157 * @param name the name of the class to load.
158 * @return the loaded class or <tt>null</tt> if it could not be
159 * loaded.
160 **/
161 public static Class loadClassUsingClass(Class clazz, String name, SecureAction action)
162 {
163 Class loadedClass = null;
164
165 while (clazz != null)
166 {
167 // Get the class loader of the current class object.
168 ClassLoader loader = action.getClassLoader(clazz);
169 // A null class loader represents the system class loader.
170 loader = (loader == null) ? action.getSystemClassLoader() : loader;
171 try
172 {
173 return loader.loadClass(name);
174 }
175 catch (ClassNotFoundException ex)
176 {
177 // Ignore and try interface class loaders.
178 }
179
180 // Try to see if we can load the class from
181 // one of the class's implemented interface
182 // class loaders.
183 Class[] ifcs = clazz.getInterfaces();
184 for (int i = 0; i < ifcs.length; i++)
185 {
186 loadedClass = loadClassUsingClass(ifcs[i], name, action);
187 if (loadedClass != null)
188 {
189 return loadedClass;
190 }
191 }
192
193 // Try to see if we can load the class from
194 // the super class class loader.
195 clazz = clazz.getSuperclass();
196 }
197
198 return null;
199 }
200
201 /**
202 * This method determines if the requesting bundle is able to cast
203 * the specified service reference based on class visibility rules
204 * of the underlying modules.
205 * @param requester The bundle requesting the service.
206 * @param ref The service in question.
207 * @return <tt>true</tt> if the requesting bundle is able to case
208 * the service object to a known type.
209 **/
210 public static boolean isServiceAssignable(Bundle requester, ServiceReference ref)
211 {
212 // Boolean flag.
213 boolean allow = true;
214 // Get the service's objectClass property.
215 String[] objectClass = (String[]) ref.getProperty(FelixConstants.OBJECTCLASS);
216
217 // The the service reference is not assignable when the requesting
218 // bundle is wired to a different version of the service object.
219 // NOTE: We are pessimistic here, if any class in the service's
220 // objectClass is not usable by the requesting bundle, then we
221 // disallow the service reference.
222 for (int classIdx = 0; (allow) && (classIdx < objectClass.length); classIdx++)
223 {
224 if (!ref.isAssignableTo(requester, objectClass[classIdx]))
225 {
226 allow = false;
227 }
228 }
229 return allow;
230 }
231
232 public static ICapability getSatisfyingCapability(IModule m, IRequirement req)
233 {
234 ICapability[] caps = m.getCapabilities();
235 for (int i = 0; (caps != null) && (i < caps.length); i++)
236 {
237 if (caps[i].getNamespace().equals(req.getNamespace()) &&
238 req.isSatisfied(caps[i]))
239 {
240 return caps[i];
241 }
242 }
243 return null;
244 }
245
246 /**
247 * Returns all the capabilities from a module that has a specified namespace.
248 *
249 * @param module module providing capabilities
250 * @param namespace capability namespace
251 * @return array of matching capabilities or empty if none found
252 */
253 public static ICapability[] getCapabilityByNamespace(IModule module, String namespace)
254 {
255 final List matching = new ArrayList();
256 final ICapability[] caps = module.getCapabilities();
257 for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
258 {
259 if (caps[capIdx].getNamespace().equals(namespace))
260 {
261 matching.add(caps[capIdx]);
262 }
263 }
264 return (ICapability[]) matching.toArray(new ICapability[matching.size()]);
265 }
266
267 public static IWire getWire(IModule m, String name)
268 {
269 IWire[] wires = m.getWires();
270 for (int i = 0; (wires != null) && (i < wires.length); i++)
271 {
272 if (wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
273 ((Capability) wires[i].getCapability()).getPackageName().equals(name))
274 {
275 return wires[i];
276 }
277 }
278 return null;
279 }
280
281 private static final byte encTab[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
282 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52,
283 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64,
284 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
285 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x30, 0x31,
286 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f };
287
288 private static final byte decTab[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1,
289 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
290 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1,
291 -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
292 -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
293 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
294 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
295 48, 49, 50, 51, -1, -1, -1, -1, -1 };
296
297 public static String base64Encode(String s) throws IOException
298 {
299 return encode(s.getBytes(), 0);
300 }
301
302 /**
303 * Encode a raw byte array to a Base64 String.
304 *
305 * @param in Byte array to encode.
306 * @param len Length of Base64 lines. 0 means no line breaks.
307 **/
308 public static String encode(byte[] in, int len) throws IOException
309 {
310 ByteArrayOutputStream baos = null;
311 ByteArrayInputStream bais = null;
312 try
313 {
314 baos = new ByteArrayOutputStream();
315 bais = new ByteArrayInputStream(in);
316 encode(bais, baos, len);
317 // ASCII byte array to String
318 return (new String(baos.toByteArray()));
319 }
320 finally
321 {
322 if (baos != null)
323 {
324 baos.close();
325 }
326 if (bais != null)
327 {
328 bais.close();
329 }
330 }
331 }
332
333 public static void encode(InputStream in, OutputStream out, int len)
334 throws IOException
335 {
336
337 // Check that length is a multiple of 4 bytes
338 if (len % 4 != 0)
339 {
340 throw new IllegalArgumentException("Length must be a multiple of 4");
341 }
342
343 // Read input stream until end of file
344 int bits = 0;
345 int nbits = 0;
346 int nbytes = 0;
347 int b;
348
349 while ((b = in.read()) != -1)
350 {
351 bits = (bits << 8) | b;
352 nbits += 8;
353 while (nbits >= 6)
354 {
355 nbits -= 6;
356 out.write(encTab[0x3f & (bits >> nbits)]);
357 nbytes++;
358 // New line
359 if (len != 0 && nbytes >= len)
360 {
361 out.write(0x0d);
362 out.write(0x0a);
363 nbytes -= len;
364 }
365 }
366 }
367
368 switch (nbits)
369 {
370 case 2:
371 out.write(encTab[0x3f & (bits << 4)]);
372 out.write(0x3d); // 0x3d = '='
373 out.write(0x3d);
374 break;
375 case 4:
376 out.write(encTab[0x3f & (bits << 2)]);
377 out.write(0x3d);
378 break;
379 }
380
381 if (len != 0)
382 {
383 if (nbytes != 0)
384 {
385 out.write(0x0d);
386 out.write(0x0a);
387 }
388 out.write(0x0d);
389 out.write(0x0a);
390 }
391 }
392
393
394 private static final String DELIM_START = "${";
395 private static final String DELIM_STOP = "}";
396
397 /**
398 * <p>
399 * This method performs property variable substitution on the
400 * specified value. If the specified value contains the syntax
401 * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
402 * refers to either a configuration property or a system property,
403 * then the corresponding property value is substituted for the variable
404 * placeholder. Multiple variable placeholders may exist in the
405 * specified value as well as nested variable placeholders, which
406 * are substituted from inner most to outer most. Configuration
407 * properties override system properties.
408 * </p>
409 * @param val The string on which to perform property substitution.
410 * @param currentKey The key of the property being evaluated used to
411 * detect cycles.
412 * @param cycleMap Map of variable references used to detect nested cycles.
413 * @param configProps Set of configuration properties.
414 * @return The value of the specified string after system property substitution.
415 * @throws IllegalArgumentException If there was a syntax error in the
416 * property placeholder syntax or a recursive variable reference.
417 **/
418 public static String substVars(String val, String currentKey,
419 Map cycleMap, Properties configProps)
420 throws IllegalArgumentException
421 {
422 // If there is currently no cycle map, then create
423 // one for detecting cycles for this invocation.
424 if (cycleMap == null)
425 {
426 cycleMap = new HashMap();
427 }
428
429 // Put the current key in the cycle map.
430 cycleMap.put(currentKey, currentKey);
431
432 // Assume we have a value that is something like:
433 // "leading ${foo.${bar}} middle ${baz} trailing"
434
435 // Find the first ending '}' variable delimiter, which
436 // will correspond to the first deepest nested variable
437 // placeholder.
438 int stopDelim = val.indexOf(DELIM_STOP);
439
440 // Find the matching starting "${" variable delimiter
441 // by looping until we find a start delimiter that is
442 // greater than the stop delimiter we have found.
443 int startDelim = val.indexOf(DELIM_START);
444 while (stopDelim >= 0)
445 {
446 int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
447 if ((idx < 0) || (idx > stopDelim))
448 {
449 break;
450 }
451 else if (idx < stopDelim)
452 {
453 startDelim = idx;
454 }
455 }
456
457 // If we do not have a start or stop delimiter, then just
458 // return the existing value.
459 if ((startDelim < 0) && (stopDelim < 0))
460 {
461 return val;
462 }
463 // At this point, we found a stop delimiter without a start,
464 // so throw an exception.
465 else if (((startDelim < 0) || (startDelim > stopDelim))
466 && (stopDelim >= 0))
467 {
468 throw new IllegalArgumentException(
469 "stop delimiter with no start delimiter: "
470 + val);
471 }
472
473 // At this point, we have found a variable placeholder so
474 // we must perform a variable substitution on it.
475 // Using the start and stop delimiter indices, extract
476 // the first, deepest nested variable placeholder.
477 String variable =
478 val.substring(startDelim + DELIM_START.length(), stopDelim);
479
480 // Verify that this is not a recursive variable reference.
481 if (cycleMap.get(variable) != null)
482 {
483 throw new IllegalArgumentException(
484 "recursive variable reference: " + variable);
485 }
486
487 // Get the value of the deepest nested variable placeholder.
488 // Try to configuration properties first.
489 String substValue = (configProps != null)
490 ? configProps.getProperty(variable, null)
491 : null;
492 if (substValue == null)
493 {
494 // Ignore unknown property values.
495 substValue = System.getProperty(variable, "");
496 }
497
498 // Remove the found variable from the cycle map, since
499 // it may appear more than once in the value and we don't
500 // want such situations to appear as a recursive reference.
501 cycleMap.remove(variable);
502
503 // Append the leading characters, the substituted value of
504 // the variable, and the trailing characters to get the new
505 // value.
506 val = val.substring(0, startDelim)
507 + substValue
508 + val.substring(stopDelim + DELIM_STOP.length(), val.length());
509
510 // Now perform substitution again, since there could still
511 // be substitutions to make.
512 val = substVars(val, currentKey, cycleMap, configProps);
513
514 // Return the value.
515 return val;
516 }
517
518 /**
519 * Checks if the provided module definition declares a fragment host.
520 *
521 * @param module the module to check
522 * @return <code>true</code> if the module declares a fragment host, <code>false</code>
523 * otherwise.
524 */
525 public static boolean isFragment(IModule module)
526 {
527 Map headerMap = module.getHeaders();
528 return headerMap.containsKey(Constants.FRAGMENT_HOST);
529 }
530
531
532 //
533 // The following substring-related code was lifted and modified
534 // from the LDAP parser code.
535 //
536
537 public static String[] parseSubstring(String target)
538 {
539 List pieces = new ArrayList();
540 StringBuffer ss = new StringBuffer();
541 // int kind = SIMPLE; // assume until proven otherwise
542 boolean wasStar = false; // indicates last piece was a star
543 boolean leftstar = false; // track if the initial piece is a star
544 boolean rightstar = false; // track if the final piece is a star
545
546 int idx = 0;
547
548 // We assume (sub)strings can contain leading and trailing blanks
549 loop: for (;;)
550 {
551 if (idx >= target.length())
552 {
553 if (wasStar)
554 {
555 // insert last piece as "" to handle trailing star
556 rightstar = true;
557 }
558 else
559 {
560 pieces.add(ss.toString());
561 // accumulate the last piece
562 // note that in the case of
563 // (cn=); this might be
564 // the string "" (!=null)
565 }
566 ss.setLength(0);
567 break loop;
568 }
569
570 char c = target.charAt(idx++);
571 if (c == '*')
572 {
573 if (wasStar)
574 {
575 // encountered two successive stars;
576 // I assume this is illegal
577 throw new IllegalArgumentException("Invalid filter string: " + target);
578 }
579 if (ss.length() > 0)
580 {
581 pieces.add(ss.toString()); // accumulate the pieces
582 // between '*' occurrences
583 }
584 ss.setLength(0);
585 // if this is a leading star, then track it
586 if (pieces.size() == 0)
587 {
588 leftstar = true;
589 }
590 wasStar = true;
591 }
592 else
593 {
594 wasStar = false;
595 ss.append(c);
596 }
597 }
598 if (leftstar || rightstar || pieces.size() > 1)
599 {
600 // insert leading and/or trailing "" to anchor ends
601 if (rightstar)
602 {
603 pieces.add("");
604 }
605 if (leftstar)
606 {
607 pieces.add(0, "");
608 }
609 }
610 return (String[]) pieces.toArray(new String[pieces.size()]);
611 }
612
613 public static boolean checkSubstring(String[] pieces, String s)
614 {
615 // Walk the pieces to match the string
616 // There are implicit stars between each piece,
617 // and the first and last pieces might be "" to anchor the match.
618 // assert (pieces.length > 1)
619 // minimal case is <string>*<string>
620
621 boolean result = true;
622 int len = pieces.length;
623
624 int index = 0;
625
626 loop: for (int i = 0; i < len; i++)
627 {
628 String piece = pieces[i];
629
630 // If this is the first piece, then make sure the
631 // string starts with it.
632 if (i == 0)
633 {
634 if (!s.startsWith(piece))
635 {
636 result = false;
637 break loop;
638 }
639 }
640
641 // If this is the last piece, then make sure the
642 // string ends with it.
643 if (i == len - 1)
644 {
645 if (s.endsWith(piece))
646 {
647 result = true;
648 }
649 else
650 {
651 result = false;
652 }
653 break loop;
654 }
655
656 // If this is neither the first or last piece, then
657 // make sure the string contains it.
658 if ((i > 0) && (i < (len - 1)))
659 {
660 index = s.indexOf(piece, index);
661 if (index < 0)
662 {
663 result = false;
664 break loop;
665 }
666 }
667
668 // Move string index beyond the matching piece.
669 index += piece.length();
670 }
671
672 return result;
673 }
674 }