TestConfigurationListeners.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 2014, 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.FilePermission;
25import java.io.IOException;
26import java.security.AccessControlException;
27import java.security.CodeSource;
28import java.security.Permission;
29import java.security.PermissionCollection;
30import java.security.Permissions;
31import java.security.Policy;
32import java.security.ProtectionDomain;
33import java.util.Arrays;
34import java.util.Collections;
35import java.util.ConcurrentModificationException;
36import java.util.Enumeration;
37import java.util.HashSet;
38import java.util.PropertyPermission;
39import java.util.Set;
40import java.util.concurrent.atomic.AtomicLong;
41import java.util.logging.LogManager;
42import java.util.logging.LoggingPermission;
43
44/**
45 * @test
46 * @bug 8043306
47 * @summary tests LogManager.addConfigurationListener and
48 *                LogManager.removeConfigurationListener;
49 * @build TestConfigurationListeners
50 * @run main/othervm TestConfigurationListeners UNSECURE
51 * @run main/othervm TestConfigurationListeners PERMISSION
52 * @run main/othervm TestConfigurationListeners SECURE
53 * @author danielfuchs
54 */
55public class TestConfigurationListeners {
56
57    /**
58     * We will test add and remove ConfigurationListeners in 3 configurations.
59     * UNSECURE: No security manager.
60     * SECURE: With the security manager present - and the required
61     *         LoggingPermission("control") granted.
62     * PERMISSION: With the security manager present - and the required
63     *         LoggingPermission("control") *not* granted. Here we will
64     *         test that the expected security permission is thrown.
65     */
66    public static enum TestCase {
67        UNSECURE, SECURE, PERMISSION;
68        public void run(String name) throws Exception {
69            System.out.println("Running test case: " + name());
70            switch (this) {
71                case UNSECURE:
72                    testUnsecure(name);
73                    break;
74                case SECURE:
75                    testSecure(name);
76                    break;
77                case PERMISSION:
78                    testPermission(name);
79                    break;
80                default:
81                    throw new Error("Unknown test case: "+this);
82            }
83        }
84        public String loggerName(String name) {
85            return name;
86        }
87    }
88
89    public static void main(String... args) throws Exception {
90
91
92        if (args == null || args.length == 0) {
93            args = new String[] {
94                TestCase.UNSECURE.name(),
95                TestCase.SECURE.name(),
96            };
97        }
98
99        for (String testName : args) {
100            TestCase test = TestCase.valueOf(testName);
101            test.run(test.loggerName("foo.bar"));
102        }
103    }
104
105    /**
106     * Test without security manager.
107     * @param loggerName The logger to use.
108     * @throws Exception if the test fails.
109     */
110    public static void testUnsecure(String loggerName) throws Exception {
111        if (System.getSecurityManager() != null) {
112            throw new Error("Security manager is set");
113        }
114        test(loggerName);
115    }
116
117    /**
118     * Test with security manager.
119     * @param loggerName The logger to use.
120     * @throws Exception if the test fails.
121     */
122    public static void testSecure(String loggerName) throws Exception {
123        if (System.getSecurityManager() != null) {
124            throw new Error("Security manager is already set");
125        }
126        Policy.setPolicy(new SimplePolicy(TestCase.SECURE));
127        System.setSecurityManager(new SecurityManager());
128        test(loggerName);
129    }
130
131    /**
132     * Test the LoggingPermission("control") is required.
133     * @param loggerName The logger to use.
134     */
135    public static void testPermission(String loggerName) {
136        TestConfigurationListener run = new TestConfigurationListener(
137                TestCase.PERMISSION.toString());
138        if (System.getSecurityManager() != null) {
139            throw new Error("Security manager is already set");
140        }
141        Policy.setPolicy(new SimplePolicy(TestCase.PERMISSION));
142        System.setSecurityManager(new SecurityManager());
143
144        try {
145            LogManager.getLogManager().addConfigurationListener(run);
146            throw new RuntimeException("addConfigurationListener: Permission not checked!");
147        } catch (AccessControlException x) {
148            boolean ok = false;
149            if (x.getPermission() instanceof LoggingPermission) {
150                if ("control".equals(x.getPermission().getName())) {
151                    System.out.println("addConfigurationListener: Got expected exception: " + x);
152                    ok = true;
153                }
154            }
155            if (!ok) {
156                throw new RuntimeException("addConfigurationListener: Unexpected exception: "+x, x);
157            }
158        }
159
160        try {
161            LogManager.getLogManager().removeConfigurationListener(run);
162            throw new RuntimeException("removeConfigurationListener: Permission not checked!");
163        } catch (AccessControlException x) {
164            boolean ok = false;
165            if (x.getPermission() instanceof LoggingPermission) {
166                if ("control".equals(x.getPermission().getName())) {
167                    System.out.println("removeConfigurationListener: Got expected exception: " + x);
168                    ok = true;
169                }
170            }
171            if (!ok) {
172                throw new RuntimeException("removeConfigurationListener: Unexpected exception: "+x, x);
173            }
174        }
175        try {
176            LogManager.getLogManager().addConfigurationListener(null);
177            throw new RuntimeException(
178                    "addConfigurationListener(null): Expected NPE not thrown.");
179        } catch (NullPointerException npe) {
180            System.out.println("Got expected NPE: "+npe);
181        }
182
183        try {
184            LogManager.getLogManager().removeConfigurationListener(null);
185            throw new RuntimeException(
186                    "removeConfigurationListener(null): Expected NPE not thrown.");
187        } catch (NullPointerException npe) {
188            System.out.println("Got expected NPE: "+npe);
189        }
190
191
192    }
193
194
195    static class TestConfigurationListener implements Runnable {
196        final AtomicLong  count = new AtomicLong(0);
197        final String name;
198        TestConfigurationListener(String name) {
199            this.name = name;
200        }
201        @Override
202        public void run() {
203            final long times = count.incrementAndGet();
204            System.out.println("Configured \"" + name + "\": " + times);
205        }
206    }
207
208    static class ConfigurationListenerException extends RuntimeException {
209        public ConfigurationListenerException(String msg) {
210            super(msg);
211        }
212
213        @Override
214        public String toString() {
215            return this.getClass().getName() + ": " + getMessage();
216        }
217    }
218    static class ConfigurationListenerError extends Error {
219        public ConfigurationListenerError(String msg) {
220            super(msg);
221        }
222
223        @Override
224        public String toString() {
225            return this.getClass().getName() + ": " + getMessage();
226        }
227    }
228
229    static class ThrowingConfigurationListener extends TestConfigurationListener {
230
231        final boolean error;
232        public ThrowingConfigurationListener(String name, boolean error) {
233            super(name);
234            this.error = error;
235        }
236
237        @Override
238        public void run() {
239            if (error)
240                throw new ConfigurationListenerError(name);
241            else
242                throw new ConfigurationListenerException(name);
243        }
244
245        @Override
246        public String toString() {
247            final Class<? extends Throwable> type =
248                    error ? ConfigurationListenerError.class
249                          : ConfigurationListenerException.class;
250            return  type.getName()+ ": " + name;
251        }
252
253    }
254
255    private static void expect(TestConfigurationListener listener, long value) {
256        final long got = listener.count.longValue();
257        if (got != value) {
258            throw new RuntimeException(listener.name + " expected " + value +", got " + got);
259        }
260
261    }
262
263    public interface ThrowingConsumer<T, I extends Exception> {
264        public void accept(T t) throws I;
265    }
266
267    public static class ReadConfiguration implements ThrowingConsumer<LogManager, IOException> {
268
269        @Override
270        public void accept(LogManager t) throws IOException {
271            t.readConfiguration();
272        }
273
274    }
275
276    public static void test(String loggerName) throws Exception {
277        System.out.println("Starting test for " + loggerName);
278        test("m.readConfiguration()", (m) -> m.readConfiguration());
279        test("m.readConfiguration(new ByteArrayInputStream(new byte[0]))",
280                (m) -> m.readConfiguration(new ByteArrayInputStream(new byte[0])));
281        System.out.println("Test passed for " + loggerName);
282    }
283
284    public static void test(String testName,
285            ThrowingConsumer<LogManager, IOException> readConfiguration) throws Exception {
286
287
288        System.out.println("\nBEGIN " + testName);
289        LogManager m = LogManager.getLogManager();
290
291        final TestConfigurationListener l1 = new TestConfigurationListener("l#1");
292        final TestConfigurationListener l2 = new TestConfigurationListener("l#2");
293        final TestConfigurationListener l3 = new ThrowingConfigurationListener("l#3", false);
294        final TestConfigurationListener l4 = new ThrowingConfigurationListener("l#4", true);
295        final TestConfigurationListener l5 = new ThrowingConfigurationListener("l#5", false);
296
297        final Set<String> expectedExceptions =
298                Collections.unmodifiableSet(
299                        new HashSet<>(Arrays.asList(
300                                l3.toString(), l4.toString(), l5.toString())));
301
302        m.addConfigurationListener(l1);
303        m.addConfigurationListener(l2);
304        expect(l1, 0);
305        expect(l2, 0);
306
307        readConfiguration.accept(m);
308        expect(l1, 1);
309        expect(l2, 1);
310        m.addConfigurationListener(l1);
311        expect(l1, 1);
312        expect(l2, 1);
313        readConfiguration.accept(m);
314        expect(l1, 2);
315        expect(l2, 2);
316        m.removeConfigurationListener(l1);
317        expect(l1, 2);
318        expect(l2, 2);
319        readConfiguration.accept(m);
320        expect(l1, 2);
321        expect(l2, 3);
322        m.removeConfigurationListener(l1);
323        expect(l1, 2);
324        expect(l2, 3);
325        readConfiguration.accept(m);
326        expect(l1, 2);
327        expect(l2, 4);
328        m.removeConfigurationListener(l2);
329        expect(l1, 2);
330        expect(l2, 4);
331        readConfiguration.accept(m);
332        expect(l1, 2);
333        expect(l2, 4);
334
335        // l1 and l2 should no longer be present: this should not fail...
336        m.removeConfigurationListener(l1);
337        m.removeConfigurationListener(l1);
338        m.removeConfigurationListener(l2);
339        m.removeConfigurationListener(l2);
340        expect(l1, 2);
341        expect(l2, 4);
342
343        readConfiguration.accept(m);
344        expect(l1, 2);
345        expect(l2, 4);
346
347        // add back l1 and l2
348        m.addConfigurationListener(l1);
349        m.addConfigurationListener(l2);
350        expect(l1, 2);
351        expect(l2, 4);
352
353        readConfiguration.accept(m);
354        expect(l1, 3);
355        expect(l2, 5);
356
357        m.removeConfigurationListener(l1);
358        m.removeConfigurationListener(l2);
359        expect(l1, 3);
360        expect(l2, 5);
361
362        readConfiguration.accept(m);
363        expect(l1, 3);
364        expect(l2, 5);
365
366        // Check the behavior when listeners throw exceptions
367        // l3, l4, and l5 will throw an error/exception.
368        // The first that is raised will be propagated, after all listeners
369        // have been invoked. The other exceptions will be added to the
370        // suppressed list.
371        //
372        // We will check that all listeners have been invoked and that we
373        // have the set of 3 exceptions expected from l3, l4, l5.
374        //
375        m.addConfigurationListener(l4);
376        m.addConfigurationListener(l1);
377        m.addConfigurationListener(l2);
378        m.addConfigurationListener(l3);
379        m.addConfigurationListener(l5);
380
381        try {
382            readConfiguration.accept(m);
383            throw new RuntimeException("Excpected exception/error not raised");
384        } catch(ConfigurationListenerException | ConfigurationListenerError t) {
385            final Set<String> received = new HashSet<>();
386            received.add(t.toString());
387            for (Throwable s : t.getSuppressed()) {
388                received.add(s.toString());
389            }
390            System.out.println("Received exceptions: " + received);
391            if (!expectedExceptions.equals(received)) {
392                throw new RuntimeException(
393                        "List of received exceptions differs from expected:"
394                                + "\n\texpected: " + expectedExceptions
395                                + "\n\treceived: " + received);
396            }
397        }
398        expect(l1, 4);
399        expect(l2, 6);
400
401        m.removeConfigurationListener(l1);
402        m.removeConfigurationListener(l2);
403        m.removeConfigurationListener(l3);
404        m.removeConfigurationListener(l4);
405        m.removeConfigurationListener(l5);
406        readConfiguration.accept(m);
407        expect(l1, 4);
408        expect(l2, 6);
409
410
411        try {
412            m.addConfigurationListener(null);
413            throw new RuntimeException(
414                    "addConfigurationListener(null): Expected NPE not thrown.");
415        } catch (NullPointerException npe) {
416            System.out.println("Got expected NPE: "+npe);
417        }
418
419        try {
420            m.removeConfigurationListener(null);
421            throw new RuntimeException(
422                    "removeConfigurationListener(null): Expected NPE not thrown.");
423        } catch (NullPointerException npe) {
424            System.out.println("Got expected NPE: "+npe);
425        }
426
427        System.out.println("END " + testName+"\n");
428
429    }
430
431
432    static final class PermissionsBuilder {
433        final Permissions perms;
434        public PermissionsBuilder() {
435            this(new Permissions());
436        }
437        public PermissionsBuilder(Permissions perms) {
438            this.perms = perms;
439        }
440        public PermissionsBuilder add(Permission p) {
441            perms.add(p);
442            return this;
443        }
444        public PermissionsBuilder addAll(PermissionCollection col) {
445            if (col != null) {
446                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
447                    perms.add(e.nextElement());
448                }
449            }
450            return this;
451        }
452        public Permissions toPermissions() {
453            final PermissionsBuilder builder = new PermissionsBuilder();
454            builder.addAll(perms);
455            return builder.perms;
456        }
457    }
458
459    public static class SimplePolicy extends Policy {
460
461        final Permissions permissions;
462        public SimplePolicy(TestCase test) {
463            permissions = new Permissions();
464            if (test != TestCase.PERMISSION) {
465                permissions.add(new LoggingPermission("control", null));
466                permissions.add(new PropertyPermission("java.util.logging.config.class", "read"));
467                permissions.add(new PropertyPermission("java.util.logging.config.file", "read"));
468                permissions.add(new PropertyPermission("java.home", "read"));
469                permissions.add(new FilePermission("<<ALL FILES>>", "read"));
470            }
471        }
472
473        @Override
474        public boolean implies(ProtectionDomain domain, Permission permission) {
475            return permissions.implies(permission);
476        }
477
478        @Override
479        public PermissionCollection getPermissions(CodeSource codesource) {
480            return new PermissionsBuilder().addAll(permissions).toPermissions();
481        }
482
483        @Override
484        public PermissionCollection getPermissions(ProtectionDomain domain) {
485            return new PermissionsBuilder().addAll(permissions).toPermissions();
486        }
487    }
488
489}
490