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