1 /*
   2  * Copyright (c) 2015, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.io.ByteArrayInputStream;
  24 import java.io.ByteArrayOutputStream;
  25 import java.io.FileOutputStream;
  26 import java.io.IOException;
  27 import java.nio.file.Path;
  28 import java.nio.file.Paths;
  29 import java.security.CodeSource;
  30 import java.security.Permission;
  31 import java.security.PermissionCollection;
  32 import java.security.Permissions;
  33 import java.security.Policy;
  34 import java.security.ProtectionDomain;
  35 import java.util.Arrays;
  36 import java.util.Enumeration;
  37 import java.util.Objects;
  38 import java.util.Properties;
  39 import java.util.concurrent.Callable;
  40 import java.util.concurrent.atomic.AtomicBoolean;
  41 import java.util.concurrent.atomic.AtomicLong;
  42 import java.util.function.BiFunction;
  43 import java.util.function.Function;
  44 import java.util.logging.Handler;
  45 import java.util.logging.Level;
  46 import java.util.logging.LogManager;
  47 import java.util.logging.LogRecord;
  48 import java.util.logging.Logger;
  49 import java.util.logging.LoggingPermission;
  50 
  51 /**
  52  * @test
  53  * @bug 8033661
  54  * @summary tests LogManager.updateConfiguration(InputStream, Function) method
  55  * @run main/othervm SimpleUpdateConfigWithInputStreamTest UNSECURE
  56  * @run main/othervm SimpleUpdateConfigWithInputStreamTest SECURE
  57  * @author danielfuchs
  58  */
  59 public class SimpleUpdateConfigWithInputStreamTest {
  60 
  61     /**
  62      * We will test updateConfiguration in
  63      * two configurations:
  64      * UNSECURE: No security manager.
  65      * SECURE: With the security manager present - and the required
  66      *         permissions granted.
  67      */
  68     public static enum TestCase {
  69         UNSECURE, SECURE;
  70         public void execute(Runnable run) {
  71             System.out.println("Running test case: " + name());
  72             try {
  73                Configure.setUp(this);
  74                Configure.doPrivileged(run, SimplePolicy.allowControl);
  75             } finally {
  76                Configure.doPrivileged(() -> {
  77                    try {
  78                        setSystemProperty("java.util.logging.config.file", null);
  79                        LogManager.getLogManager().readConfiguration();
  80                        System.gc();
  81                    } catch (Exception x) {
  82                        throw new RuntimeException(x);
  83                    }
  84                }, SimplePolicy.allowAll);
  85             }
  86         }
  87     }
  88 
  89     public static class MyHandler extends Handler {
  90         static final AtomicLong seq = new AtomicLong();
  91         long count = seq.incrementAndGet();
  92 
  93         @Override
  94         public void publish(LogRecord record) {
  95         }
  96 
  97         @Override
  98         public void flush() {
  99         }
 100 
 101         @Override
 102         public void close() throws SecurityException {
 103         }
 104 
 105         @Override
 106         public String toString() {
 107             return super.toString() + "("+count+")";
 108         }
 109 
 110     }
 111 
 112     static String storePropertyToFile(String name, Properties props)
 113         throws Exception {
 114         return Configure.callPrivileged(() -> {
 115             String scratch = System.getProperty("user.dir", ".");
 116             Path p = Paths.get(scratch, name);
 117             try (FileOutputStream fos = new FileOutputStream(p.toFile())) {
 118                 props.store(fos, name);
 119             }
 120             return p.toString();
 121         }, SimplePolicy.allowAll);
 122     }
 123 
 124     static void setSystemProperty(String name, String value)
 125         throws Exception {
 126         Configure.doPrivileged(() -> {
 127             if (value == null)
 128                 System.clearProperty(name);
 129             else
 130                 System.setProperty(name, value);
 131         }, SimplePolicy.allowAll);
 132     }
 133 
 134     static String trim(String value) {
 135         return value == null ? null : value.trim();
 136     }
 137 
 138 
 139     /**
 140      * Tests one of the configuration defined above.
 141      * <p>
 142      * This is the main test method (the rest is infrastructure).
 143      */
 144     static void testUpdateConfiguration() {
 145         try {
 146             // manager initialized with default configuration.
 147             LogManager manager = LogManager.getLogManager();
 148 
 149             // Test default configuration. It should not have
 150             // any value for "com.foo.level" and "com.foo.handlers"
 151             assertEquals(null, manager.getProperty("com.foo.level"),
 152                 "com.foo.level in default configuration");
 153             assertEquals(null, manager.getProperty("com.foo.handlers"),
 154                 "com.foo.handlers in default configuration");
 155 
 156             // Create a logging configuration file that contains
 157             // com.foo.level=FINEST
 158             // and set "java.util.logging.config.file" to this file.
 159             Properties props = new Properties();
 160             props.setProperty("com.foo.level", "FINEST");
 161 
 162             // Update configuration with props
 163             // then test that the new configuration has
 164             // com.foo.level=FINEST
 165             // and nothing for com.foo.handlers
 166             Configure.updateConfigurationWith(props, null);
 167             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 168                 "com.foo.level in " + props);
 169             assertEquals(null, manager.getProperty("com.foo.handlers"),
 170                 "com.foo.handlers in " + props);
 171 
 172             // call updateConfiguration with an empty stream.
 173             // check that the new configuration no longer has
 174             // any value for com.foo.level, and still no value
 175             // for com.foo.handlers
 176             Configure.updateConfigurationWith(new Properties(), null);
 177             assertEquals(null, manager.getProperty("com.foo.level"),
 178                     "com.foo.level in default configuration");
 179             assertEquals(null, manager.getProperty("com.foo.handlers"),
 180                 "com.foo.handlers in default configuration");
 181 
 182             // creates the com.foo logger, check it has
 183             // the default config: no level, and no handlers
 184             final Logger logger = Logger.getLogger("com.foo");
 185             assertEquals(null, logger.getLevel(),
 186                 "Logger.getLogger(\"com.foo\").getLevel()");
 187             assertDeepEquals(new Handler[0], logger.getHandlers(),
 188                     "Logger.getLogger(\"com.foo\").getHandlers()");
 189 
 190             // call updateConfiguration with 'props'
 191             // check that the configuration has
 192             // com.foo.level=FINEST
 193             // and nothing for com.foo.handlers
 194             // check that the logger has now a FINEST level and still
 195             // no handlers
 196             Configure.updateConfigurationWith(props, null);
 197             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 198                 "com.foo.level in " + props);
 199             assertEquals(Level.FINEST, logger.getLevel(),
 200                 "Logger.getLogger(\"com.foo\").getLevel()");
 201             assertDeepEquals(new Handler[0], logger.getHandlers(),
 202                     "Logger.getLogger(\"com.foo\").getHandlers()");
 203             assertEquals(null, manager.getProperty("com.foo.handlers"),
 204                 "com.foo.handlers in " + props);
 205 
 206             // Calls updateConfiguration with a lambda whose effect should
 207             // be to set the FINER level on the "com.foo" logger.
 208             // Check that the new configuration has
 209             // com.foo.level=FINER
 210             // and nothing for com.foo.handlers
 211             // check that the logger has now a FINER level and still
 212             // no handlers
 213             Configure.updateConfigurationWith(props,
 214                     (k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n));
 215             assertEquals("FINER", manager.getProperty("com.foo.level"),
 216                 "com.foo.level set to FINER by updateConfiguration");
 217             assertEquals(Level.FINER, logger.getLevel(),
 218                 "Logger.getLogger(\"com.foo\").getLevel()");
 219             assertDeepEquals(new Handler[0], logger.getHandlers(),
 220                     "Logger.getLogger(\"com.foo\").getHandlers()");
 221             assertEquals(null, manager.getProperty("com.foo.handlers"),
 222                 "com.foo.handlers in " + props);
 223 
 224             // Calls updateConfiguration with a lambda whose effect is a noop.
 225             // This should not change the configuration, so
 226             // check that the new configuration still has
 227             // com.foo.level=FINER
 228             // and nothing for com.foo.handlers
 229             // check that the logger still has FINER level and still
 230             // no handlers
 231             Configure.updateConfigurationWith(props,
 232                     (k) -> ((o, n) -> o));
 233             assertEquals("FINER", manager.getProperty("com.foo.level"),
 234                 "com.foo.level preserved by updateConfiguration");
 235             assertEquals(Level.FINER, logger.getLevel(),
 236                 "Logger.getLogger(\"com.foo\").getLevel()");
 237             assertDeepEquals(new Handler[0], logger.getHandlers(),
 238                     "Logger.getLogger(\"com.foo\").getHandlers()");
 239             assertEquals(null, manager.getProperty("com.foo.handlers"),
 240                 "com.foo.handlers in " + props);
 241 
 242             // Calls updateConfiguration with a lambda whose effect is to
 243             // take all values from the new configuration.
 244             // This should update the configuration to what is in props, so
 245             // check that the new configuration has
 246             // com.foo.level=FINEST
 247             // and nothing for com.foo.handlers
 248             // check that the logger now has FINEST level and still
 249             // no handlers
 250             Configure.updateConfigurationWith(props,
 251                     (k) -> ((o, n) -> n));
 252             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 253                 "com.foo.level updated by updateConfiguration");
 254             assertEquals(Level.FINEST, logger.getLevel(),
 255                 "Logger.getLogger(\"com.foo\").getLevel()");
 256             assertDeepEquals(new Handler[0], logger.getHandlers(),
 257                     "Logger.getLogger(\"com.foo\").getHandlers()");
 258             assertEquals(null, manager.getProperty("com.foo.handlers"),
 259                 "com.foo.handlers in " + props);
 260 
 261             // now set a handler on the com.foo logger.
 262             MyHandler h = new MyHandler();
 263             logger.addHandler(h);
 264             assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
 265                     "Logger.getLogger(\"com.foo\").getHandlers()");
 266 
 267             // Calls updateConfiguration with a lambda whose effect should
 268             // be to set the FINER level on the "com.foo" logger, and
 269             // take the value from props for everything else.
 270             // Check that the new configuration has
 271             // com.foo.level=FINER
 272             // and nothing for com.foo.handlers
 273             // check that the logger has now a FINER level, but that its
 274             // handlers are still present and have not been reset
 275             // since neither the old nor new configuration defined them.
 276             Configure.updateConfigurationWith(props,
 277                     (k) -> ("com.foo.level".equals(k) ? (o, n) -> "FINER" : (o, n) -> n));
 278             assertEquals("FINER", manager.getProperty("com.foo.level"),
 279                 "com.foo.level set to FINER by updateConfiguration");
 280             assertEquals(Level.FINER, logger.getLevel(),
 281                 "Logger.getLogger(\"com.foo\").getLevel()");
 282             assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
 283                     "Logger.getLogger(\"com.foo\").getHandlers()");
 284             assertEquals(null, manager.getProperty("com.foo.handlers"),
 285                 "com.foo.handlers in " + props);
 286 
 287             // now add some configuration for com.foo.handlers
 288             props.setProperty("com.foo.handlers", MyHandler.class.getName());
 289 
 290             // we didn't call updateConfiguration, so just changing the
 291             // content of props should have had no effect.
 292             assertEquals("FINER", manager.getProperty("com.foo.level"),
 293                 "com.foo.level set to FINER by updateConfiguration");
 294             assertEquals(Level.FINER, logger.getLevel(),
 295                 "Logger.getLogger(\"com.foo\").getLevel()");
 296             assertEquals(null,
 297                     manager.getProperty("com.foo.handlers"),
 298                     "manager.getProperty(\"com.foo.handlers\")");
 299             assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
 300                     "Logger.getLogger(\"com.foo\").getHandlers()");
 301 
 302             // Calls updateConfiguration with a lambda whose effect is a noop.
 303             // This should not change the current configuration, so
 304             // check that the new configuration still has
 305             // com.foo.level=FINER
 306             // and nothing for com.foo.handlers
 307             // check that the logger still has FINER level and still
 308             // has its handlers and that they haven't been reset.
 309             Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o));
 310             assertEquals("FINER", manager.getProperty("com.foo.level"),
 311                 "com.foo.level set to FINER by updateConfiguration");
 312             assertEquals(Level.FINER, logger.getLevel(),
 313                 "Logger.getLogger(\"com.foo\").getLevel()");
 314             assertEquals(null,
 315                     manager.getProperty("com.foo.handlers"),
 316                     "manager.getProperty(\"com.foo.handlers\")");
 317             assertDeepEquals(new Handler[] {h}, logger.getHandlers(),
 318                     "Logger.getLogger(\"com.foo\").getHandlers()");
 319 
 320             // Calls updateConfiguration with a lambda whose effect is to
 321             // take all values from the new configuration.
 322             // This should update the configuration to what is in props, so
 323             // check that the new configuration has
 324             // com.foo.level=FINEST
 325             // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
 326             // check that the logger now has FINEST level
 327             // and a new handler instance, since the old config
 328             // had no handlers for com.foo and the new config has one.
 329             Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
 330             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 331                 "com.foo.level updated by updateConfiguration");
 332             assertEquals(Level.FINEST, logger.getLevel(),
 333                 "Logger.getLogger(\"com.foo\").getLevel()");
 334             assertEquals(MyHandler.class.getName(),
 335                     manager.getProperty("com.foo.handlers"),
 336                     "manager.getProperty(\"com.foo.handlers\")");
 337             Handler[] loggerHandlers = logger.getHandlers().clone();
 338             assertEquals(1, loggerHandlers.length,
 339                     "Logger.getLogger(\"com.foo\").getHandlers().length");
 340             assertEquals(MyHandler.class, loggerHandlers[0].getClass(),
 341                     "Logger.getLogger(\"com.foo\").getHandlers()[0].getClass()");
 342             assertEquals(h.count + 1, ((MyHandler)logger.getHandlers()[0]).count,
 343                     "Logger.getLogger(\"com.foo\").getHandlers()[0].count");
 344 
 345             // Calls updateConfiguration with a lambda whose effect is a noop.
 346             // This should not change the current configuration, so
 347             // check that the new configuration still has
 348             // com.foo.level=FINEST
 349             // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
 350             // check that the logger still has FINEST level and still
 351             // has its handlers and that they haven't been reset.
 352             Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o));
 353             assertDeepEquals(loggerHandlers, logger.getHandlers(),
 354                     "Logger.getLogger(\"com.foo\").getHandlers()");
 355             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 356                 "com.foo.level updated by updateConfiguration");
 357             assertEquals(Level.FINEST, logger.getLevel(),
 358                 "Logger.getLogger(\"com.foo\").getLevel()");
 359             assertEquals(MyHandler.class.getName(),
 360                     manager.getProperty("com.foo.handlers"),
 361                     "manager.getProperty(\"com.foo.handlers\")");
 362 
 363             // Calls updateConfiguration with a lambda whose effect is to
 364             // take all values from the new configuration.
 365             // Because the content of the props hasn't changed, then
 366             // it should also be a noop.
 367             // check that the new configuration still has
 368             // com.foo.level=FINEST
 369             // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
 370             // check that the logger still has FINEST level and still
 371             // has its handlers and that they haven't been reset.
 372             Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
 373             assertDeepEquals(loggerHandlers, logger.getHandlers(),
 374                     "Logger.getLogger(\"com.foo\").getHandlers()");
 375             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 376                 "com.foo.level updated by updateConfiguration");
 377             assertEquals(Level.FINEST, logger.getLevel(),
 378                 "Logger.getLogger(\"com.foo\").getLevel()");
 379             assertEquals(MyHandler.class.getName(),
 380                     manager.getProperty("com.foo.handlers"),
 381                     "manager.getProperty(\"com.foo.handlers\")");
 382 
 383             // Calls updateConfiguration with a null lambda, whose effect is to
 384             // take all values from the new configuration.
 385             // Because the content of the props hasn't changed, then
 386             // it should also be a noop.
 387             // check that the new configuration still has
 388             // com.foo.level=FINEST
 389             // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
 390             // check that the logger still has FINEST level and still
 391             // has its handlers and that they haven't been reset.
 392             Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
 393             assertDeepEquals(loggerHandlers, logger.getHandlers(),
 394                     "Logger.getLogger(\"com.foo\").getHandlers()");
 395             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 396                 "com.foo.level updated by updateConfiguration");
 397             assertEquals(Level.FINEST, logger.getLevel(),
 398                 "Logger.getLogger(\"com.foo\").getLevel()");
 399             assertEquals(MyHandler.class.getName(),
 400                     manager.getProperty("com.foo.handlers"),
 401                     "manager.getProperty(\"com.foo.handlers\")");
 402 
 403             // now remove com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
 404             // from the configuration file.
 405             props.remove("com.foo.handlers");
 406 
 407             // Calls updateConfiguration with a lambda whose effect is a noop.
 408             // This should not change the current configuration, so
 409             // check that the new configuration still has
 410             // com.foo.level=FINEST
 411             // com.foo.handlers=SimpleUpdateConfigWithInputStreamTest$MyHandler
 412             // check that the logger still has FINEST level and still
 413             // has its handlers and that they haven't been reset.
 414             Configure.updateConfigurationWith(props, (k) -> ((o, n) -> o));
 415             assertDeepEquals(loggerHandlers, logger.getHandlers(),
 416                     "Logger.getLogger(\"com.foo\").getHandlers()");
 417             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 418                 "com.foo.level updated by updateConfiguration");
 419             assertEquals(Level.FINEST, logger.getLevel(),
 420                 "Logger.getLogger(\"com.foo\").getLevel()");
 421             assertEquals(MyHandler.class.getName(),
 422                     manager.getProperty("com.foo.handlers"),
 423                     "manager.getProperty(\"com.foo.handlers\")");
 424 
 425             // Calls updateConfiguration with a lambda whose effect is to
 426             // take all values from the new configuration.
 427             // This should update the configuration to what is in props, so
 428             // check that the new configuration has
 429             // com.foo.level=FINEST
 430             // and nothing for com.foo.handlers
 431             // check that the logger still has FINEST level
 432             // and no handlers, since the old config
 433             // had an handler for com.foo and the new config doesn't.
 434             Configure.updateConfigurationWith(props, (k) -> ((o, n) -> n));
 435             assertDeepEquals(new Handler[0], logger.getHandlers(),
 436                     "Logger.getLogger(\"com.foo\").getHandlers()");
 437             assertEquals("FINEST", manager.getProperty("com.foo.level"),
 438                 "com.foo.level updated by updateConfiguration");
 439             assertEquals(Level.FINEST, logger.getLevel(),
 440                 "Logger.getLogger(\"com.foo\").getLevel()");
 441             assertEquals(null,
 442                     manager.getProperty("com.foo.handlers"),
 443                     "manager.getProperty(\"com.foo.handlers\")");
 444 
 445 
 446         } catch (RuntimeException | Error r) {
 447             throw r;
 448         } catch (Exception x) {
 449             throw new RuntimeException(x);
 450         }
 451     }
 452 
 453     public static void main(String[] args) throws Exception {
 454         if (args == null || args.length == 0) {
 455             args = new String[] { "UNSECURE", "SECURE" };
 456         }
 457         for (String test : args) {
 458             TestCase.valueOf(test).execute(SimpleUpdateConfigWithInputStreamTest::testUpdateConfiguration);
 459         }
 460     }
 461 
 462     static class Configure {
 463         static Policy policy = null;
 464         static void setUp(TestCase test) {
 465             switch (test) {
 466                 case SECURE:
 467                     if (policy == null && System.getSecurityManager() != null) {
 468                         throw new IllegalStateException("SecurityManager already set");
 469                     } else if (policy == null) {
 470                         policy = new SimplePolicy(TestCase.SECURE);
 471                         Policy.setPolicy(policy);
 472                         System.setSecurityManager(new SecurityManager());
 473                     }
 474                     if (System.getSecurityManager() == null) {
 475                         throw new IllegalStateException("No SecurityManager.");
 476                     }
 477                     if (policy == null) {
 478                         throw new IllegalStateException("policy not configured");
 479                     }
 480                     break;
 481                 case UNSECURE:
 482                     if (System.getSecurityManager() != null) {
 483                         throw new IllegalStateException("SecurityManager already set");
 484                     }
 485                     break;
 486                 default:
 487                     throw new InternalError("No such testcase: " + test);
 488             }
 489         }
 490 
 491         static void updateConfigurationWith(Properties propertyFile,
 492                 Function<String,BiFunction<String,String,String>> remapper) {
 493             try {
 494                 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
 495                 propertyFile.store(bytes, propertyFile.getProperty("test.name"));
 496                 ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
 497                 LogManager.getLogManager().updateConfiguration(bais, remapper);
 498             } catch (IOException ex) {
 499                 throw new RuntimeException(ex);
 500             }
 501         }
 502 
 503         static void doPrivileged(Runnable run, ThreadLocal<AtomicBoolean> granter) {
 504             final boolean old = granter.get().getAndSet(true);
 505             try {
 506                 run.run();
 507             } finally {
 508                 granter.get().set(old);
 509             }
 510         }
 511         static <T> T callPrivileged(Callable<T> call,
 512                 ThreadLocal<AtomicBoolean> granter) throws Exception {
 513             final boolean old = granter.get().getAndSet(true);
 514             try {
 515                 return call.call();
 516             } finally {
 517                 granter.get().set(old);
 518             }
 519         }
 520     }
 521 
 522     static final class TestAssertException extends RuntimeException {
 523         TestAssertException(String msg) {
 524             super(msg);
 525         }
 526     }
 527 
 528     private static void assertEquals(long expected, long received, String msg) {
 529         if (expected != received) {
 530             throw new TestAssertException("Unexpected result for " + msg
 531                     + ".\n\texpected: " + expected
 532                     +  "\n\tactual:   " + received);
 533         } else {
 534             System.out.println("Got expected " + msg + ": " + received);
 535         }
 536     }
 537 
 538     private static void assertEquals(String expected, String received, String msg) {
 539         if (!Objects.equals(expected, received)) {
 540             throw new TestAssertException("Unexpected result for " + msg
 541                     + ".\n\texpected: " + expected
 542                     +  "\n\tactual:   " + received);
 543         } else {
 544             System.out.println("Got expected " + msg + ": " + received);
 545         }
 546     }
 547 
 548     private static void assertEquals(Object expected, Object received, String msg) {
 549         if (!Objects.equals(expected, received)) {
 550             throw new TestAssertException("Unexpected result for " + msg
 551                     + ".\n\texpected: " + expected
 552                     +  "\n\tactual:   " + received);
 553         } else {
 554             System.out.println("Got expected " + msg + ": " + received);
 555         }
 556     }
 557 
 558     public static String deepToString(Object o) {
 559         if (o == null) {
 560             return "null";
 561         } else if (o.getClass().isArray()) {
 562             String s;
 563             if (o instanceof Object[])
 564                 s = Arrays.deepToString((Object[]) o);
 565             else if (o instanceof byte[])
 566                 s = Arrays.toString((byte[]) o);
 567             else if (o instanceof short[])
 568                 s = Arrays.toString((short[]) o);
 569             else if (o instanceof int[])
 570                 s = Arrays.toString((int[]) o);
 571             else if (o instanceof long[])
 572                 s = Arrays.toString((long[]) o);
 573             else if (o instanceof char[])
 574                 s = Arrays.toString((char[]) o);
 575             else if (o instanceof float[])
 576                 s = Arrays.toString((float[]) o);
 577             else if (o instanceof double[])
 578                 s = Arrays.toString((double[]) o);
 579             else if (o instanceof boolean[])
 580                 s = Arrays.toString((boolean[]) o);
 581             else
 582                 s = o.toString();
 583             return s;
 584         } else {
 585             return o.toString();
 586         }
 587     }
 588 
 589     private static void assertDeepEquals(Object expected, Object received, String msg) {
 590         if (!Objects.deepEquals(expected, received)) {
 591             throw new TestAssertException("Unexpected result for " + msg
 592                     + ".\n\texpected: " + deepToString(expected)
 593                     +  "\n\tactual:   " + deepToString(received));
 594         } else {
 595             System.out.println("Got expected " + msg + ": " + deepToString(received));
 596         }
 597     }
 598 
 599     final static class PermissionsBuilder {
 600         final Permissions perms;
 601         public PermissionsBuilder() {
 602             this(new Permissions());
 603         }
 604         public PermissionsBuilder(Permissions perms) {
 605             this.perms = perms;
 606         }
 607         public PermissionsBuilder add(Permission p) {
 608             perms.add(p);
 609             return this;
 610         }
 611         public PermissionsBuilder addAll(PermissionCollection col) {
 612             if (col != null) {
 613                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 614                     perms.add(e.nextElement());
 615                 }
 616             }
 617             return this;
 618         }
 619         public Permissions toPermissions() {
 620             final PermissionsBuilder builder = new PermissionsBuilder();
 621             builder.addAll(perms);
 622             return builder.perms;
 623         }
 624     }
 625 
 626     public static class SimplePolicy extends Policy {
 627 
 628         final Permissions basic;
 629         final Permissions control;
 630         final Permissions all;
 631         public final static ThreadLocal<AtomicBoolean> allowAll =
 632                 new ThreadLocal<AtomicBoolean>() {
 633             @Override
 634             protected AtomicBoolean initialValue() {
 635                 return new AtomicBoolean();
 636             }
 637         };
 638         public final static ThreadLocal<AtomicBoolean> allowControl =
 639                 new ThreadLocal<AtomicBoolean>() {
 640             @Override
 641             protected AtomicBoolean initialValue() {
 642                 return new AtomicBoolean();
 643             }
 644         };
 645         public SimplePolicy(TestCase test) {
 646             basic = new Permissions();
 647             control = new Permissions();
 648             control.add(new LoggingPermission("control", null));
 649 
 650             // these are used for configuring the test itself...
 651             all = new Permissions();
 652             all.add(new java.security.AllPermission());
 653 
 654         }
 655 
 656         @Override
 657         public boolean implies(ProtectionDomain domain, Permission permission) {
 658             return getPermissions(domain).implies(permission);
 659         }
 660 
 661         public PermissionCollection permissions() {
 662             PermissionsBuilder builder = new PermissionsBuilder();
 663             if (allowAll.get().get()) {
 664                 builder.addAll(all);
 665             } else {
 666                 builder.addAll(basic);
 667                 if (allowControl.get().get()) {
 668                     builder.addAll(control);
 669                 }
 670             }
 671             return builder.toPermissions();
 672         }
 673 
 674         @Override
 675         public PermissionCollection getPermissions(CodeSource codesource) {
 676             return permissions();
 677         }
 678 
 679         @Override
 680         public PermissionCollection getPermissions(ProtectionDomain domain) {
 681             return permissions();
 682         }
 683     }
 684 
 685 }