1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.io; 27 28 import java.security.AccessController; 29 import java.security.PrivilegedAction; 30 import java.security.Security; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.Optional; 35 import java.util.function.Function; 36 37 import jdk.internal.misc.SharedSecrets; 38 39 /** 40 * Filter classes, array lengths, and graph metrics during deserialization. 41 * If set on an {@link ObjectInputStream}, the {@link #checkInput checkInput(FilterInfo)} 42 * method is called to validate classes, the length of each array, 43 * the number of objects being read from the stream, the depth of the graph, 44 * and the total number of bytes read from the stream. 45 * <p> 46 * A filter can be set via {@link ObjectInputStream#setObjectInputFilter setObjectInputFilter} 47 * for an individual ObjectInputStream. 48 * A filter can be set via {@link Config#setSerialFilter(ObjectInputFilter) Config.setSerialFilter} 49 * to affect every {@code ObjectInputStream} that does not otherwise set a filter. 50 * <p> 51 * A filter determines whether the arguments are {@link Status#ALLOWED ALLOWED} 52 * or {@link Status#REJECTED REJECTED} and should return the appropriate status. 53 * If the filter cannot determine the status it should return 54 * {@link Status#UNDECIDED UNDECIDED}. 55 * Filters should be designed for the specific use case and expected types. 56 * A filter designed for a particular use may be passed a class that is outside 57 * of the scope of the filter. If the purpose of the filter is to black-list classes 58 * then it can reject a candidate class that matches and report UNDECIDED for others. 59 * A filter may be called with class equals {@code null}, {@code arrayLength} equal -1, 60 * the depth, number of references, and stream size and return a status 61 * that reflects only one or only some of the values. 62 * This allows a filter to specific about the choice it is reporting and 63 * to use other filters without forcing either allowed or rejected status. 64 * 65 * <p> 66 * Typically, a custom filter should check if a process-wide filter 67 * is configured and defer to it if so. For example, 68 * <pre>{@code 69 * ObjectInputFilter.Status checkInput(FilterInfo info) { 70 * ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter(); 71 * if (serialFilter != null) { 72 * ObjectInputFilter.Status status = serialFilter.checkInput(info); 73 * if (status != ObjectInputFilter.Status.UNDECIDED) { 74 * // The process-wide filter overrides this filter 75 * return status; 76 * } 77 * } 78 * if (info.serialClass() != null && 79 * Remote.class.isAssignableFrom(info.serialClass())) { 80 * return Status.REJECTED; // Do not allow Remote objects 81 * } 82 * return Status.UNDECIDED; 83 * } 84 *}</pre> 85 * <p> 86 * Unless otherwise noted, passing a {@code null} argument to a 87 * method in this interface and its nested classes will cause a 88 * {@link NullPointerException} to be thrown. 89 * 90 * @see ObjectInputStream#setObjectInputFilter(ObjectInputFilter) 91 * @since 9 92 */ 93 @FunctionalInterface 94 public interface ObjectInputFilter { 95 96 /** 97 * Check the class, array length, number of object references, depth, 98 * stream size, and other available filtering information. 99 * Implementations of this method check the contents of the object graph being created 100 * during deserialization. The filter returns {@link Status#ALLOWED Status.ALLOWED}, 101 * {@link Status#REJECTED Status.REJECTED}, or {@link Status#UNDECIDED Status.UNDECIDED}. 102 * 103 * @param filterInfo provides information about the current object being deserialized, 104 * if any, and the status of the {@link ObjectInputStream} 105 * @return {@link Status#ALLOWED Status.ALLOWED} if accepted, 106 * {@link Status#REJECTED Status.REJECTED} if rejected, 107 * {@link Status#UNDECIDED Status.UNDECIDED} if undecided. 108 */ 109 Status checkInput(FilterInfo filterInfo); 110 111 /** 112 * FilterInfo provides access to information about the current object 113 * being deserialized and the status of the {@link ObjectInputStream}. 114 * @since 9 115 */ 116 interface FilterInfo { 117 /** 118 * The class of an object being deserialized. 119 * For arrays, it is the array type. 120 * For example, the array class name of a 2 dimensional array of strings is 121 * "{@code [[Ljava.lang.String;}". 122 * To check the array's element type, iteratively use 123 * {@link Class#getComponentType() Class.getComponentType} while the result 124 * is an array and then check the class. 125 * The {@code serialClass is null} in the case where a new object is not being 126 * created and to give the filter a chance to check the depth, number of 127 * references to existing objects, and the stream size. 128 * 129 * @return class of an object being deserialized; may be null 130 */ 131 Class<?> serialClass(); 132 133 /** 134 * The number of array elements when deserializing an array of the class. 135 * 136 * @return the non-negative number of array elements when deserializing 137 * an array of the class, otherwise -1 138 */ 139 long arrayLength(); 140 141 /** 142 * The current depth. 143 * The depth starts at {@code 1} and increases for each nested object and 144 * decrements when each nested object returns. 145 * 146 * @return the current depth 147 */ 148 long depth(); 149 150 /** 151 * The current number of object references. 152 * 153 * @return the non-negative current number of object references 154 */ 155 long references(); 156 157 /** 158 * The current number of bytes consumed. 159 * @implSpec {@code streamBytes} is implementation specific 160 * and may not be directly related to the object in the stream 161 * that caused the callback. 162 * 163 * @return the non-negative current number of bytes consumed 164 */ 165 long streamBytes(); 166 } 167 168 /** 169 * The status of a check on the class, array length, number of references, 170 * depth, and stream size. 171 * 172 * @since 9 173 */ 174 enum Status { 175 /** 176 * The status is undecided, not allowed and not rejected. 177 */ 178 UNDECIDED, 179 /** 180 * The status is allowed. 181 */ 182 ALLOWED, 183 /** 184 * The status is rejected. 185 */ 186 REJECTED; 187 } 188 189 /** 190 * A utility class to set and get the process-wide filter or create a filter 191 * from a pattern string. If a process-wide filter is set, it will be 192 * used for each {@link ObjectInputStream} that does not set its own filter. 193 * <p> 194 * When setting the filter, it should be stateless and idempotent, 195 * reporting the same result when passed the same arguments. 196 * <p> 197 * The filter is configured during the initialization of the {@code ObjectInputFilter.Config} 198 * class. For example, by calling {@link #getSerialFilter() Config.getSerialFilter}. 199 * If the system property {@code jdk.serialFilter} is defined, it is used 200 * to configure the filter. 201 * If the system property is not defined, and the {@link java.security.Security} 202 * property {@code jdk.serialFilter} is defined then it is used to configure the filter. 203 * Otherwise, the filter is not configured during initialization. 204 * The syntax for each property is the same as for the 205 * {@link #createFilter(String) createFilter} method. 206 * If a filter is not configured, it can be set with 207 * {@link #setSerialFilter(ObjectInputFilter) Config.setSerialFilter}. 208 * 209 * @since 9 210 */ 211 final class Config { 212 /* No instances. */ 213 private Config() {} 214 215 /** 216 * Lock object for process-wide filter. 217 */ 218 private final static Object serialFilterLock = new Object(); 219 220 /** 221 * Debug: Logger 222 */ 223 private final static System.Logger configLog; 224 225 /** 226 * Logger for debugging. 227 */ 228 static void filterLog(System.Logger.Level level, String msg, Object... args) { 229 if (configLog != null) { 230 configLog.log(level, msg, args); 231 } 232 } 233 234 /** 235 * The name for the process-wide deserialization filter. 236 * Used as a system property and a java.security.Security property. 237 */ 238 private final static String SERIAL_FILTER_PROPNAME = "jdk.serialFilter"; 239 240 /** 241 * The process-wide filter; may be null. 242 * Lookup the filter in java.security.Security or 243 * the system property. 244 */ 245 private final static ObjectInputFilter configuredFilter; 246 247 static { 248 configuredFilter = AccessController 249 .doPrivileged((PrivilegedAction<ObjectInputFilter>) () -> { 250 String props = System.getProperty(SERIAL_FILTER_PROPNAME); 251 if (props == null) { 252 props = Security.getProperty(SERIAL_FILTER_PROPNAME); 253 } 254 if (props != null) { 255 System.Logger log = 256 System.getLogger("java.io.serialization"); 257 log.log(System.Logger.Level.INFO, 258 "Creating serialization filter from {0}", props); 259 try { 260 return createFilter(props); 261 } catch (RuntimeException re) { 262 log.log(System.Logger.Level.ERROR, 263 "Error configuring filter: {0}", re); 264 } 265 } 266 return null; 267 }); 268 configLog = (configuredFilter != null) ? System.getLogger("java.io.serialization") : null; 269 270 // Setup shared secrets for RegistryImpl to use. 271 SharedSecrets.setJavaObjectInputFilterAccess(Config::createFilter2); 272 } 273 274 /** 275 * Current configured filter. 276 */ 277 private static ObjectInputFilter serialFilter = configuredFilter; 278 279 /** 280 * Returns the process-wide serialization filter or {@code null} if not configured. 281 * 282 * @return the process-wide serialization filter or {@code null} if not configured 283 */ 284 public static ObjectInputFilter getSerialFilter() { 285 synchronized (serialFilterLock) { 286 return serialFilter; 287 } 288 } 289 290 /** 291 * Set the process-wide filter if it has not already been configured or set. 292 * 293 * @param filter the serialization filter to set as the process-wide filter; not null 294 * @throws SecurityException if there is security manager and the 295 * {@code SerializablePermission("serialFilter")} is not granted 296 * @throws IllegalStateException if the filter has already been set {@code non-null} 297 */ 298 public static void setSerialFilter(ObjectInputFilter filter) { 299 Objects.requireNonNull(filter, "filter"); 300 SecurityManager sm = System.getSecurityManager(); 301 if (sm != null) { 302 sm.checkPermission(ObjectStreamConstants.SERIAL_FILTER_PERMISSION); 303 } 304 synchronized (serialFilterLock) { 305 if (serialFilter != null) { 306 throw new IllegalStateException("Serial filter can only be set once"); 307 } 308 serialFilter = filter; 309 } 310 } 311 312 /** 313 * Returns an ObjectInputFilter from a string of patterns. 314 * <p> 315 * Patterns are separated by ";" (semicolon). Whitespace is significant and 316 * is considered part of the pattern. 317 * If a pattern includes an equals assignment, "{@code =}" it sets a limit. 318 * If a limit appears more than once the last value is used. 319 * <ul> 320 * <li>maxdepth={@code value} - the maximum depth of a graph</li> 321 * <li>maxrefs={@code value} - the maximum number of internal references</li> 322 * <li>maxbytes={@code value} - the maximum number of bytes in the input stream</li> 323 * <li>maxarray={@code value} - the maximum array length allowed</li> 324 * </ul> 325 * <p> 326 * Other patterns match or reject class or package name 327 * as returned from {@link Class#getName() Class.getName()} and 328 * if an optional module name is present 329 * {@link Module#getName() class.getModule().getName()}. 330 * Note that for arrays the element type is used in the pattern, 331 * not the array type. 332 * <ul> 333 * <li>If the pattern starts with "!", the class is rejected if the remaining pattern is matched; 334 * otherwise the class is allowed if the pattern matches. 335 * <li>If the pattern contains "/", the non-empty prefix up to the "/" is the module name; 336 * if the module name matches the module name of the class then 337 * the remaining pattern is matched with the class name. 338 * If there is no "/", the module name is not compared. 339 * <li>If the pattern ends with ".**" it matches any class in the package and all subpackages. 340 * <li>If the pattern ends with ".*" it matches any class in the package. 341 * <li>If the pattern ends with "*", it matches any class with the pattern as a prefix. 342 * <li>If the pattern is equal to the class name, it matches. 343 * <li>Otherwise, the pattern is not matched. 344 * </ul> 345 * <p> 346 * The resulting filter performs the limit checks and then 347 * tries to match the class, if any. If any of the limits are exceeded, 348 * the filter returns {@link Status#REJECTED Status.REJECTED}. 349 * If the class is an array type, the class to be matched is the element type. 350 * Arrays of any number of dimensions are treated the same as the element type. 351 * For example, a pattern of "{@code !example.Foo}", 352 * rejects creation of any instance or array of {@code example.Foo}. 353 * The first pattern that matches, working from left to right, determines 354 * the {@link Status#ALLOWED Status.ALLOWED} 355 * or {@link Status#REJECTED Status.REJECTED} result. 356 * If the limits are not exceeded and no pattern matches the class, 357 * the result is {@link Status#UNDECIDED Status.UNDECIDED}. 358 * 359 * @param pattern the pattern string to parse; not null 360 * @return a filter to check a class being deserialized; 361 * {@code null} if no patterns 362 * @throws IllegalArgumentException if the pattern string is illegal or 363 * malformed and cannot be parsed. 364 * In particular, if any of the following is true: 365 * <ul> 366 * <li> if a limit is missing the name or the name is not one of 367 * "maxdepth", "maxrefs", "maxbytes", or "maxarray" 368 * <li> if the value of the limit can not be parsed by 369 * {@link Long#parseLong Long.parseLong} or is negative 370 * <li> if the pattern contains "/" and the module name is missing 371 * or the remaining pattern is empty 372 * <li> if the package is missing for ".*" and ".**" 373 * </ul> 374 */ 375 public static ObjectInputFilter createFilter(String pattern) { 376 Objects.requireNonNull(pattern, "pattern"); 377 return Global.createFilter(pattern, true); 378 } 379 380 /** 381 * Returns an ObjectInputFilter from a string of patterns that 382 * checks only the length for arrays, not the component type. 383 * 384 * @param pattern the pattern string to parse; not null 385 * @return a filter to check a class being deserialized; 386 * {@code null} if no patterns 387 */ 388 static ObjectInputFilter createFilter2(String pattern) { 389 Objects.requireNonNull(pattern, "pattern"); 390 return Global.createFilter(pattern, false); 391 } 392 393 /** 394 * Implementation of ObjectInputFilter that performs the checks of 395 * the process-wide serialization filter. If configured, it will be 396 * used for all ObjectInputStreams that do not set their own filters. 397 * 398 */ 399 final static class Global implements ObjectInputFilter { 400 /** 401 * The pattern used to create the filter. 402 */ 403 private final String pattern; 404 /** 405 * The list of class filters. 406 */ 407 private final List<Function<Class<?>, Status>> filters; 408 /** 409 * Maximum allowed bytes in the stream. 410 */ 411 private long maxStreamBytes; 412 /** 413 * Maximum depth of the graph allowed. 414 */ 415 private long maxDepth; 416 /** 417 * Maximum number of references in a graph. 418 */ 419 private long maxReferences; 420 /** 421 * Maximum length of any array. 422 */ 423 private long maxArrayLength; 424 /** 425 * True to check the component type for arrays. 426 */ 427 private final boolean checkComponentType; 428 429 /** 430 * Returns an ObjectInputFilter from a string of patterns. 431 * 432 * @param pattern the pattern string to parse 433 * @param checkComponentType true if the filter should check 434 * the component type of arrays 435 * @return a filter to check a class being deserialized; 436 * {@code null} if no patterns 437 * @throws IllegalArgumentException if the parameter is malformed 438 * if the pattern is missing the name, the long value 439 * is not a number or is negative. 440 */ 441 static ObjectInputFilter createFilter(String pattern, boolean checkComponentType) { 442 try { 443 return new Global(pattern, checkComponentType); 444 } catch (UnsupportedOperationException uoe) { 445 // no non-empty patterns 446 return null; 447 } 448 } 449 450 /** 451 * Construct a new filter from the pattern String. 452 * 453 * @param pattern a pattern string of filters 454 * @param checkComponentType true if the filter should check 455 * the component type of arrays 456 * @throws IllegalArgumentException if the pattern is malformed 457 * @throws UnsupportedOperationException if there are no non-empty patterns 458 */ 459 private Global(String pattern, boolean checkComponentType) { 460 boolean hasLimits = false; 461 this.pattern = pattern; 462 this.checkComponentType = checkComponentType; 463 464 maxArrayLength = Long.MAX_VALUE; // Default values are unlimited 465 maxDepth = Long.MAX_VALUE; 466 maxReferences = Long.MAX_VALUE; 467 maxStreamBytes = Long.MAX_VALUE; 468 469 String[] patterns = pattern.split(";"); 470 filters = new ArrayList<>(patterns.length); 471 for (int i = 0; i < patterns.length; i++) { 472 String p = patterns[i]; 473 int nameLen = p.length(); 474 if (nameLen == 0) { 475 continue; 476 } 477 if (parseLimit(p)) { 478 // If the pattern contained a limit setting, i.e. type=value 479 hasLimits = true; 480 continue; 481 } 482 boolean negate = p.charAt(0) == '!'; 483 int poffset = negate ? 1 : 0; 484 485 // isolate module name, if any 486 int slash = p.indexOf('/', poffset); 487 if (slash == poffset) { 488 throw new IllegalArgumentException("module name is missing in: \"" + pattern + "\""); 489 } 490 final String moduleName = (slash >= 0) ? p.substring(poffset, slash) : null; 491 poffset = (slash >= 0) ? slash + 1 : poffset; 492 493 final Function<Class<?>, Status> patternFilter; 494 if (p.endsWith("*")) { 495 // Wildcard cases 496 if (p.endsWith(".*")) { 497 // Pattern is a package name with a wildcard 498 final String pkg = p.substring(poffset, nameLen - 2); 499 if (pkg.isEmpty()) { 500 throw new IllegalArgumentException("package missing in: \"" + pattern + "\""); 501 } 502 if (negate) { 503 // A Function that fails if the class starts with the pattern, otherwise don't care 504 patternFilter = c -> matchesPackage(c, pkg) ? Status.REJECTED : Status.UNDECIDED; 505 } else { 506 // A Function that succeeds if the class starts with the pattern, otherwise don't care 507 patternFilter = c -> matchesPackage(c, pkg) ? Status.ALLOWED : Status.UNDECIDED; 508 } 509 } else if (p.endsWith(".**")) { 510 // Pattern is a package prefix with a double wildcard 511 final String pkgs = p.substring(poffset, nameLen - 2); 512 if (pkgs.length() < 2) { 513 throw new IllegalArgumentException("package missing in: \"" + pattern + "\""); 514 } 515 if (negate) { 516 // A Function that fails if the class starts with the pattern, otherwise don't care 517 patternFilter = c -> c.getName().startsWith(pkgs) ? Status.REJECTED : Status.UNDECIDED; 518 } else { 519 // A Function that succeeds if the class starts with the pattern, otherwise don't care 520 patternFilter = c -> c.getName().startsWith(pkgs) ? Status.ALLOWED : Status.UNDECIDED; 521 } 522 } else { 523 // Pattern is a classname (possibly empty) with a trailing wildcard 524 final String className = p.substring(poffset, nameLen - 1); 525 if (negate) { 526 // A Function that fails if the class starts with the pattern, otherwise don't care 527 patternFilter = c -> c.getName().startsWith(className) ? Status.REJECTED : Status.UNDECIDED; 528 } else { 529 // A Function that succeeds if the class starts with the pattern, otherwise don't care 530 patternFilter = c -> c.getName().startsWith(className) ? Status.ALLOWED : Status.UNDECIDED; 531 } 532 } 533 } else { 534 final String name = p.substring(poffset); 535 if (name.isEmpty()) { 536 throw new IllegalArgumentException("class or package missing in: \"" + pattern + "\""); 537 } 538 // Pattern is a class name 539 if (negate) { 540 // A Function that fails if the class equals the pattern, otherwise don't care 541 patternFilter = c -> c.getName().equals(name) ? Status.REJECTED : Status.UNDECIDED; 542 } else { 543 // A Function that succeeds if the class equals the pattern, otherwise don't care 544 patternFilter = c -> c.getName().equals(name) ? Status.ALLOWED : Status.UNDECIDED; 545 } 546 } 547 // If there is a moduleName, combine the module name check with the package/class check 548 if (moduleName == null) { 549 filters.add(patternFilter); 550 } else { 551 filters.add(c -> moduleName.equals(c.getModule().getName()) ? patternFilter.apply(c) : Status.UNDECIDED); 552 } 553 } 554 if (filters.isEmpty() && !hasLimits) { 555 throw new UnsupportedOperationException("no non-empty patterns"); 556 } 557 } 558 559 /** 560 * Parse out a limit for one of maxarray, maxdepth, maxbytes, maxreferences. 561 * 562 * @param pattern a string with a type name, '=' and a value 563 * @return {@code true} if a limit was parsed, else {@code false} 564 * @throws IllegalArgumentException if the pattern is missing 565 * the name, the Long value is not a number or is negative. 566 */ 567 private boolean parseLimit(String pattern) { 568 int eqNdx = pattern.indexOf('='); 569 if (eqNdx < 0) { 570 // not a limit pattern 571 return false; 572 } 573 String valueString = pattern.substring(eqNdx + 1); 574 if (pattern.startsWith("maxdepth=")) { 575 maxDepth = parseValue(valueString); 576 } else if (pattern.startsWith("maxarray=")) { 577 maxArrayLength = parseValue(valueString); 578 } else if (pattern.startsWith("maxrefs=")) { 579 maxReferences = parseValue(valueString); 580 } else if (pattern.startsWith("maxbytes=")) { 581 maxStreamBytes = parseValue(valueString); 582 } else { 583 throw new IllegalArgumentException("unknown limit: " + pattern.substring(0, eqNdx)); 584 } 585 return true; 586 } 587 588 /** 589 * Parse the value of a limit and check that it is non-negative. 590 * @param string inputstring 591 * @return the parsed value 592 * @throws IllegalArgumentException if parsing the value fails or the value is negative 593 */ 594 private static long parseValue(String string) throws IllegalArgumentException { 595 // Parse a Long from after the '=' to the end 596 long value = Long.parseLong(string); 597 if (value < 0) { 598 throw new IllegalArgumentException("negative limit: " + string); 599 } 600 return value; 601 } 602 603 /** 604 * {@inheritDoc} 605 */ 606 @Override 607 public Status checkInput(FilterInfo filterInfo) { 608 if (filterInfo.references() < 0 609 || filterInfo.depth() < 0 610 || filterInfo.streamBytes() < 0 611 || filterInfo.references() > maxReferences 612 || filterInfo.depth() > maxDepth 613 || filterInfo.streamBytes() > maxStreamBytes) { 614 return Status.REJECTED; 615 } 616 617 Class<?> clazz = filterInfo.serialClass(); 618 if (clazz != null) { 619 if (clazz.isArray()) { 620 if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > maxArrayLength) { 621 // array length is too big 622 return Status.REJECTED; 623 } 624 if (!checkComponentType) { 625 // As revised; do not check the component type for arrays 626 return Status.UNDECIDED; 627 } 628 do { 629 // Arrays are decided based on the component type 630 clazz = clazz.getComponentType(); 631 } while (clazz.isArray()); 632 } 633 634 if (clazz.isPrimitive()) { 635 // Primitive types are undecided; let someone else decide 636 return Status.UNDECIDED; 637 } else { 638 // Find any filter that allowed or rejected the class 639 final Class<?> cl = clazz; 640 Optional<Status> status = filters.stream() 641 .map(f -> f.apply(cl)) 642 .filter(p -> p != Status.UNDECIDED) 643 .findFirst(); 644 return status.orElse(Status.UNDECIDED); 645 } 646 } 647 return Status.UNDECIDED; 648 } 649 650 /** 651 * Returns {@code true} if the class is in the package. 652 * 653 * @param c a class 654 * @param pkg a package name 655 * @return {@code true} if the class is in the package, 656 * otherwise {@code false} 657 */ 658 private static boolean matchesPackage(Class<?> c, String pkg) { 659 return pkg.equals(c.getPackageName()); 660 } 661 662 /** 663 * Returns the pattern used to create this filter. 664 * @return the pattern used to create this filter 665 */ 666 @Override 667 public String toString() { 668 return pattern; 669 } 670 } 671 } 672 }