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 }