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  */
  23 import java.io.ByteArrayInputStream;
  24 import java.io.FilePermission;
  25 import java.io.IOException;
  26 import java.security.AccessControlException;
  27 import java.security.CodeSource;
  28 import java.security.Permission;
  29 import java.security.PermissionCollection;
  30 import java.security.Permissions;
  31 import java.security.Policy;
  32 import java.security.ProtectionDomain;
  33 import java.util.Enumeration;
  34 import java.util.PropertyPermission;
  35 import java.util.concurrent.atomic.AtomicLong;
  36 import java.util.logging.LogManager;
  37 import java.util.logging.LoggingPermission;
  38 
  39 /**
  40  * @test
  41  * @bug 8043306
  42  * @summary tests LogManager.addConfigurationListener and
  43  *                LogManager.removeConfigurationListener;
  44  * @build TestConfigurationListeners
  45  * @run main/othervm TestConfigurationListeners UNSECURE
  46  * @run main/othervm TestConfigurationListeners PERMISSION
  47  * @run main/othervm TestConfigurationListeners SECURE
  48  * @author danielfuchs
  49  */
  50 public class TestConfigurationListeners {
  51 
  52     /**
  53      * We will test add and remove ConfigurationListeners in 3 configurations.
  54      * UNSECURE: No security manager.
  55      * SECURE: With the security manager present - and the required
  56      *         LoggingPermission("control") granted.
  57      * PERMISSION: With the security manager present - and the required
  58      *         LoggingPermission("control") *not* granted. Here we will
  59      *         test that the expected security permission is thrown.
  60      */
  61     public static enum TestCase {
  62         UNSECURE, SECURE, PERMISSION;
  63         public void run(String name) throws Exception {
  64             System.out.println("Running test case: " + name());
  65             switch (this) {
  66                 case UNSECURE:
  67                     testUnsecure(name);
  68                     break;
  69                 case SECURE:
  70                     testSecure(name);
  71                     break;
  72                 case PERMISSION:
  73                     testPermission(name);
  74                     break;
  75                 default:
  76                     throw new Error("Unknown test case: "+this);
  77             }
  78         }
  79         public String loggerName(String name) {
  80             return name;
  81         }
  82     }
  83 
  84     public static void main(String... args) throws Exception {
  85 
  86  
  87         if (args == null || args.length == 0) {
  88             args = new String[] {
  89                 TestCase.UNSECURE.name(),
  90                 TestCase.SECURE.name(),
  91             };
  92         }
  93 
  94         for (String testName : args) {
  95             TestCase test = TestCase.valueOf(testName);
  96             test.run(test.loggerName("foo.bar"));
  97         }
  98     }
  99 
 100     /**
 101      * Test without security manager.
 102      * @param loggerName The logger to use.
 103      * @throws Exception if the test fails.
 104      */
 105     public static void testUnsecure(String loggerName) throws Exception {
 106         if (System.getSecurityManager() != null) {
 107             throw new Error("Security manager is set");
 108         }
 109         test(loggerName);
 110     }
 111 
 112     /**
 113      * Test with security manager.
 114      * @param loggerName The logger to use.
 115      * @throws Exception if the test fails.
 116      */
 117     public static void testSecure(String loggerName) throws Exception {
 118         if (System.getSecurityManager() != null) {
 119             throw new Error("Security manager is already set");
 120         }
 121         Policy.setPolicy(new SimplePolicy(TestCase.SECURE));
 122         System.setSecurityManager(new SecurityManager());
 123         test(loggerName);
 124     }
 125 
 126     /**
 127      * Test the LoggingPermission("control") is required.
 128      * @param loggerName The logger to use.
 129      */
 130     public static void testPermission(String loggerName) {
 131         TestConfigurationListener run = new TestConfigurationListener(
 132                 TestCase.PERMISSION.toString());
 133         if (System.getSecurityManager() != null) {
 134             throw new Error("Security manager is already set");
 135         }
 136         Policy.setPolicy(new SimplePolicy(TestCase.PERMISSION));
 137         System.setSecurityManager(new SecurityManager());
 138         
 139         try {
 140             run = new TestConfigurationListener("[Should Be Denied]");
 141             throw new RuntimeException("ConfigurationListener constructor: Permission not checked!");
 142         } catch (AccessControlException x) {
 143             boolean ok = false;
 144             if (x.getPermission() instanceof LoggingPermission) {
 145                 if ("control".equals(x.getPermission().getName())) {
 146                     System.out.println("ConfigurationListener constructor: Got expected exception: " + x);
 147                     ok = true;
 148                 }
 149             }
 150             if (!ok) {
 151                 throw new RuntimeException("ConfigurationListener constructor: Unexpected exception: "+x, x);
 152             }
 153         }
 154         try {
 155             LogManager.getLogManager().addConfigurationListener(run);
 156             throw new RuntimeException("addConfigurationListener: Permission not checked!");
 157         } catch (AccessControlException x) {
 158             boolean ok = false;
 159             if (x.getPermission() instanceof LoggingPermission) {
 160                 if ("control".equals(x.getPermission().getName())) {
 161                     System.out.println("addConfigurationListener: Got expected exception: " + x);
 162                     ok = true;
 163                 }
 164             }
 165             if (!ok) {
 166                 throw new RuntimeException("addConfigurationListener: Unexpected exception: "+x, x);
 167             }
 168         }
 169 
 170         try {
 171             LogManager.getLogManager().removeConfigurationListener(run);
 172             throw new RuntimeException("removeConfigurationListener: Permission not checked!");
 173         } catch (AccessControlException x) {
 174             boolean ok = false;
 175             if (x.getPermission() instanceof LoggingPermission) {
 176                 if ("control".equals(x.getPermission().getName())) {
 177                     System.out.println("removeConfigurationListener: Got expected exception: " + x);
 178                     ok = true;
 179                 }
 180             }
 181             if (!ok) {
 182                 throw new RuntimeException("removeConfigurationListener: Unexpected exception: "+x, x);
 183             }
 184         }
 185         try {
 186             LogManager.getLogManager().addConfigurationListener(null);
 187             throw new RuntimeException(
 188                     "addConfigurationListener(null): Expected NPE not thrown.");
 189         } catch (NullPointerException npe) {
 190             System.out.println("Got expected NPE: "+npe);
 191         }
 192 
 193         try {
 194             LogManager.getLogManager().removeConfigurationListener(null);
 195             throw new RuntimeException(
 196                     "removeConfigurationListener(null): Expected NPE not thrown.");
 197         } catch (NullPointerException npe) {
 198             System.out.println("Got expected NPE: "+npe);
 199         }
 200 
 201 
 202     }
 203 
 204 
 205     static class TestConfigurationListener implements Runnable {
 206         final AtomicLong  count = new AtomicLong(0);
 207         final String name;
 208         TestConfigurationListener(String name) {
 209             this.name = name;
 210         }
 211         @Override
 212         public void run() {
 213             final long times = count.incrementAndGet();
 214             System.out.println("Configured \"" + name + "\": " + times);
 215         }
 216     }
 217 
 218     private static void expect(TestConfigurationListener listener, long value) {
 219         final long got = listener.count.longValue();
 220         if (got != value) {
 221             throw new RuntimeException(listener.name + " expected " + value +", got " + got);
 222         }
 223 
 224     }
 225 
 226     public interface ThrowingConsumer<T, I extends Exception> {
 227         public void accept(T t) throws I;
 228     }
 229 
 230     public static class ReadConfiguration implements ThrowingConsumer<LogManager, IOException> {
 231 
 232         @Override
 233         public void accept(LogManager t) throws IOException {
 234             t.readConfiguration();
 235         }
 236 
 237     }
 238 
 239     public static void test(String loggerName) throws Exception {
 240         System.out.println("Starting test for " + loggerName);
 241         test("m.readConfiguration()", (m) -> m.readConfiguration());
 242         test("m.readConfiguration(new ByteArrayInputStream(new byte[0]))",
 243                 (m) -> m.readConfiguration(new ByteArrayInputStream(new byte[0])));
 244         System.out.println("Test passed for " + loggerName);
 245     }
 246     
 247     public static void test(String testName,
 248             ThrowingConsumer<LogManager, IOException> readConfiguration) throws Exception {
 249 
 250 
 251         System.out.println("\nBEGIN " + testName);
 252         LogManager m = LogManager.getLogManager();
 253 
 254         final TestConfigurationListener l1 = new TestConfigurationListener("l#1");
 255         final TestConfigurationListener l2 = new TestConfigurationListener("l#2");
 256         m.addConfigurationListener(l1);
 257         m.addConfigurationListener(l2);
 258         expect(l1, 0);
 259         expect(l2, 0);
 260 
 261         readConfiguration.accept(m);
 262         expect(l1, 1);
 263         expect(l2, 1);
 264         m.addConfigurationListener(l1);
 265         expect(l1, 1);
 266         expect(l2, 1);
 267         readConfiguration.accept(m);
 268         expect(l1, 2);
 269         expect(l2, 2);
 270         m.removeConfigurationListener(l1);
 271         expect(l1, 2);
 272         expect(l2, 2);
 273         readConfiguration.accept(m);
 274         expect(l1, 2);
 275         expect(l2, 3);
 276         m.removeConfigurationListener(l1);
 277         expect(l1, 2);
 278         expect(l2, 3);
 279         readConfiguration.accept(m);
 280         expect(l1, 2);
 281         expect(l2, 4);
 282         m.removeConfigurationListener(l2);
 283         expect(l1, 2);
 284         expect(l2, 4);
 285         readConfiguration.accept(m);
 286         expect(l1, 2);
 287         expect(l2, 4);
 288 
 289         // l1 and l2 should no longer be present: this should not fail...
 290         m.removeConfigurationListener(l1);
 291         m.removeConfigurationListener(l1);
 292         m.removeConfigurationListener(l2);
 293         m.removeConfigurationListener(l2);
 294         expect(l1, 2);
 295         expect(l2, 4);
 296 
 297         readConfiguration.accept(m);
 298         expect(l1, 2);
 299         expect(l2, 4);
 300 
 301         // add back l1 and l2
 302         m.addConfigurationListener(l1);
 303         m.addConfigurationListener(l2);
 304         expect(l1, 2);
 305         expect(l2, 4);
 306 
 307         readConfiguration.accept(m);
 308         expect(l1, 3);
 309         expect(l2, 5);
 310 
 311         m.removeConfigurationListener(l1);
 312         m.removeConfigurationListener(l2);
 313         expect(l1, 3);
 314         expect(l2, 5);
 315 
 316         readConfiguration.accept(m);
 317         expect(l1, 3);
 318         expect(l2, 5);
 319 
 320         try {
 321             m.addConfigurationListener(null);
 322             throw new RuntimeException(
 323                     "addConfigurationListener(null): Expected NPE not thrown.");
 324         } catch (NullPointerException npe) {
 325             System.out.println("Got expected NPE: "+npe);
 326         }
 327 
 328         try {
 329             m.removeConfigurationListener(null);
 330             throw new RuntimeException(
 331                     "removeConfigurationListener(null): Expected NPE not thrown.");
 332         } catch (NullPointerException npe) {
 333             System.out.println("Got expected NPE: "+npe);
 334         }
 335 
 336         System.out.println("END " + testName+"\n");
 337 
 338     }
 339 
 340 
 341     final static class PermissionsBuilder {
 342         final Permissions perms;
 343         public PermissionsBuilder() {
 344             this(new Permissions());
 345         }
 346         public PermissionsBuilder(Permissions perms) {
 347             this.perms = perms;
 348         }
 349         public PermissionsBuilder add(Permission p) {
 350             perms.add(p);
 351             return this;
 352         }
 353         public PermissionsBuilder addAll(PermissionCollection col) {
 354             if (col != null) {
 355                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 356                     perms.add(e.nextElement());
 357                 }
 358             }
 359             return this;
 360         }
 361         public Permissions toPermissions() {
 362             final PermissionsBuilder builder = new PermissionsBuilder();
 363             builder.addAll(perms);
 364             return builder.perms;
 365         }
 366     }
 367 
 368     public static class SimplePolicy extends Policy {
 369 
 370         final Permissions permissions;
 371         public SimplePolicy(TestCase test) {
 372             permissions = new Permissions();
 373             if (test != TestCase.PERMISSION) {
 374                 permissions.add(new LoggingPermission("control", null));
 375                 permissions.add(new PropertyPermission("java.util.logging.config.class", "read"));
 376                 permissions.add(new PropertyPermission("java.util.logging.config.file", "read"));
 377                 permissions.add(new PropertyPermission("java.home", "read"));
 378                 permissions.add(new FilePermission("<<ALL FILES>>", "read"));
 379             }
 380         }
 381 
 382         @Override
 383         public boolean implies(ProtectionDomain domain, Permission permission) {
 384             return permissions.implies(permission);
 385         }
 386 
 387         @Override
 388         public PermissionCollection getPermissions(CodeSource codesource) {
 389             return new PermissionsBuilder().addAll(permissions).toPermissions();
 390         }
 391 
 392         @Override
 393         public PermissionCollection getPermissions(ProtectionDomain domain) {
 394             return new PermissionsBuilder().addAll(permissions).toPermissions();
 395         }
 396     }
 397 
 398 }