1 /*
   2  * Copyright (c) 2013, 2017, 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.security.AccessControlException;
  24 import java.security.Permission;
  25 import java.security.Permissions;
  26 import java.security.Policy;
  27 import java.security.ProtectionDomain;
  28 import java.util.Locale;
  29 import java.util.Objects;
  30 import java.util.PropertyPermission;
  31 import java.util.ResourceBundle;
  32 import java.util.logging.Handler;
  33 import java.util.logging.Level;
  34 import java.util.logging.LogManager;
  35 import java.util.logging.LogRecord;
  36 import java.util.logging.Logger;
  37 import java.util.logging.LoggingPermission;
  38 import resources.ListBundle;
  39 
  40 /**
  41  * @test
  42  * @bug 8013839 8189291
  43  * @summary tests Logger.setResourceBundle;
  44  * @build TestSetResourceBundle resources.ListBundle resources.ListBundle_fr
  45  * @run main/othervm TestSetResourceBundle UNSECURE
  46  * @run main/othervm TestSetResourceBundle PERMISSION
  47  * @run main/othervm TestSetResourceBundle SECURE
  48  * @author danielfuchs
  49  */
  50 public class TestSetResourceBundle {
  51 
  52     static final Policy DEFAULT_POLICY = Policy.getPolicy();
  53     static final String LIST_BUNDLE_NAME = "resources.ListBundle";
  54     static final String PROPERTY_BUNDLE_NAME = "resources.PropertyBundle";
  55 
  56     /**
  57      * A dummy handler class that we can use to check the bundle/bundle name
  58      * that was present in the last LogRecord instance published.
  59      */
  60     static final class TestHandler extends Handler {
  61         volatile ResourceBundle lastBundle = null;
  62         volatile String lastBundleName = null;
  63         @Override
  64         public void publish(LogRecord record) {
  65             lastBundle = record.getResourceBundle();
  66             lastBundleName = record.getResourceBundleName();
  67         }
  68 
  69         @Override
  70         public void flush() {
  71         }
  72 
  73         @Override
  74         public void close() throws SecurityException {
  75         }
  76     }
  77 
  78     /**
  79      * We will test setResourceBundle() in 3 configurations.
  80      * UNSECURE: No security manager.
  81      * SECURE: With the security manager present - and the required
  82      *         LoggingPermission("control") granted.
  83      * PERMISSION: With the security manager present - and the required
  84      *         LoggingPermission("control") *not* granted. Here we will
  85      *         test that the expected security permission is thrown.
  86      */
  87     public static enum TestCase {
  88         UNSECURE, SECURE, PERMISSION;
  89         public void run(String name) throws Exception {
  90             System.out.println("Running test case: " + name());
  91             switch (this) {
  92                 case UNSECURE:
  93                     testUnsecure(name);
  94                     break;
  95                 case SECURE:
  96                     testSecure(name);
  97                     break;
  98                 case PERMISSION:
  99                     testPermission(name);
 100                     break;
 101                 default:
 102                     throw new Error("Unknown test case: "+this);
 103             }
 104         }
 105         public String loggerName(String name) {
 106             return name().toLowerCase(Locale.ROOT) + "." + name;
 107         }
 108     }
 109 
 110     public static void main(String... args) throws Exception {
 111 
 112         Locale defaultLocale = Locale.getDefault();
 113 
 114         if (args == null || args.length == 0) {
 115             args = new String[] {
 116                 TestCase.UNSECURE.name(),
 117                 TestCase.SECURE.name()
 118             };
 119         }
 120 
 121         for (String testName : args) {
 122             TestCase test = TestCase.valueOf(testName);
 123             try {
 124                 test.run(test.loggerName("foo.bar"));
 125             } finally {
 126                 Locale.setDefault(defaultLocale);
 127             }
 128         }
 129     }
 130 
 131     /**
 132      * Test without security manager.
 133      * @param loggerName The logger to use.
 134      * @throws Exception if the test fails.
 135      */
 136     public static void testUnsecure(String loggerName) throws Exception {
 137         if (System.getSecurityManager() != null) {
 138             throw new Error("Security manager is set");
 139         }
 140         test(loggerName);
 141     }
 142 
 143     /**
 144      * Test with security manager.
 145      * @param loggerName The logger to use.
 146      * @throws Exception if the test fails.
 147      */
 148     public static void testSecure(String loggerName) throws Exception {
 149         if (System.getSecurityManager() != null) {
 150             throw new Error("Security manager is already set");
 151         }
 152         Policy.setPolicy(new SimplePolicy(TestCase.SECURE));
 153         System.setSecurityManager(new SecurityManager());
 154         test(loggerName);
 155     }
 156 
 157     /**
 158      * Test the LoggingPermission("control") is required.
 159      * @param loggerName The logger to use.
 160      */
 161     public static void testPermission(String loggerName) {
 162         if (System.getSecurityManager() != null) {
 163             throw new Error("Security manager is already set");
 164         }
 165         Policy.setPolicy(new SimplePolicy(TestCase.PERMISSION));
 166         System.setSecurityManager(new SecurityManager());
 167         final ResourceBundle bundle = ResourceBundle.getBundle(LIST_BUNDLE_NAME);
 168         Logger foobar = Logger.getLogger(loggerName);
 169         try {
 170             foobar.setResourceBundle(bundle);
 171             throw new RuntimeException("Permission not checked!");
 172         } catch (AccessControlException x) {
 173             if (x.getPermission() instanceof LoggingPermission) {
 174                 if ("control".equals(x.getPermission().getName())) {
 175                     System.out.println("Got expected exception: " + x);
 176                     return;
 177                 }
 178             }
 179             throw new RuntimeException("Unexpected exception: "+x, x);
 180         }
 181 
 182     }
 183 
 184     static String getBaseName(ResourceBundle bundle) {
 185         return bundle == null ? null : bundle.getBaseBundleName();
 186     }
 187 
 188     public static void test(String loggerName) throws Exception {
 189 
 190         System.out.println("Starting test for " + loggerName);
 191 
 192         final ResourceBundle bundle = ResourceBundle.getBundle(LIST_BUNDLE_NAME);
 193         Logger foobar = Logger.getLogger(loggerName);
 194 
 195         // Checks that IAE is thrown if the bundle has a null base name.
 196         try {
 197             foobar.setResourceBundle(new ListBundle());
 198             throw new RuntimeException("Expected exception not raised!");
 199         } catch (IllegalArgumentException x) {
 200             System.out.println("Got expected exception: " + x);
 201         }
 202 
 203         // Verify that resource bundle was not set.
 204         if (foobar.getResourceBundle() != null) {
 205             throw new RuntimeException("Unexpected bundle: "
 206                     + foobar.getResourceBundle());
 207         }
 208         if (foobar.getResourceBundleName() != null) {
 209             throw new RuntimeException("Unexpected bundle: "
 210                     + foobar.getResourceBundleName());
 211         }
 212 
 213         // Set acceptable resource bundle on logger.
 214         foobar.setResourceBundle(bundle);
 215 
 216         // check that the bundle has been set correctly
 217         if (bundle != foobar.getResourceBundle()) {
 218             throw new RuntimeException("Unexpected bundle: "
 219                     + foobar.getResourceBundle());
 220         }
 221         if (!Objects.equals(getBaseName(bundle), foobar.getResourceBundleName())) {
 222             throw new RuntimeException("Unexpected bundle name: "
 223                     + foobar.getResourceBundleName());
 224         }
 225 
 226         // Check that we can replace the bundle with a bundle of the same name.
 227         final ResourceBundle bundle_fr =
 228                 ResourceBundle.getBundle(LIST_BUNDLE_NAME, Locale.FRENCH);
 229         foobar.setResourceBundle(bundle_fr);
 230 
 231         if (bundle_fr != foobar.getResourceBundle()) {
 232             throw new RuntimeException("Unexpected bundle: "
 233                     + foobar.getResourceBundle());
 234         }
 235         if (!Objects.equals(getBaseName(bundle_fr), foobar.getResourceBundleName())) {
 236             throw new RuntimeException("Unexpected bundle name: "
 237                     + foobar.getResourceBundleName());
 238         }
 239 
 240         // Create a child logger
 241         final Logger foobaz = Logger.getLogger(loggerName + ".baz");
 242 
 243         if (foobar != foobaz.getParent()) {
 244             throw new RuntimeException("Unexpected parent: " +
 245                     foobaz.getParent() + " != " + foobar);
 246         }
 247 
 248         // Check that the child logger does not have a bundle set locally
 249         if (foobaz.getResourceBundle() != null) {
 250             throw new RuntimeException("Unexpected bundle: "
 251                     + foobaz.getResourceBundle());
 252         }
 253         if (foobaz.getResourceBundleName() != null) {
 254             throw new RuntimeException("Unexpected bundle: "
 255                     + foobaz.getResourceBundleName());
 256         }
 257 
 258 
 259         // Add a handler on the child logger.
 260         final TestHandler handler = new TestHandler();
 261         foobaz.addHandler(handler);
 262 
 263         // log a message on the child logger
 264         foobaz.severe("dummy");
 265 
 266         // checks that the message has been logged with the bundle
 267         // inherited from the parent logger
 268         if (!LIST_BUNDLE_NAME.equals(handler.lastBundleName)) {
 269             debugLogger(foobaz, foobar, handler);
 270             throw new RuntimeException("Unexpected bundle name: "
 271                     + handler.lastBundleName);
 272         }
 273         if (!bundle_fr.equals(handler.lastBundle)) {
 274             debugLogger(foobaz, foobar, handler);
 275             throw new RuntimeException("Unexpected bundle: "
 276                     + handler.lastBundle);
 277         }
 278 
 279         // Check that we can get set a bundle on the child logger
 280         // using Logger.getLogger.
 281         final Logger foobaz2 = Logger.getLogger(loggerName + ".baz", PROPERTY_BUNDLE_NAME);
 282         if (foobaz2 != foobaz) {
 283             throw new RuntimeException("Unexpected logger: " + foobaz2 + " != " + foobaz);
 284         }
 285         if (foobar != foobaz.getParent()) {
 286             throw new RuntimeException("Unexpected parent: " +
 287                     foobaz.getParent() + " != " + foobar);
 288         }
 289 
 290         // check that the child logger has the correct bundle.
 291         // it should no longer inherit it from its parent.
 292         if (!PROPERTY_BUNDLE_NAME.equals(foobaz2.getResourceBundleName())) {
 293             throw new RuntimeException("Unexpected bundle name: "
 294                     + foobaz2.getResourceBundleName());
 295         }
 296 
 297         if (!PROPERTY_BUNDLE_NAME.equals(foobaz2.getResourceBundle().getBaseBundleName())) {
 298             throw new RuntimeException("Unexpected bundle name: "
 299                     + foobaz2.getResourceBundle().getBaseBundleName());
 300         }
 301 
 302         boolean found = false;
 303         for (Handler h : foobaz2.getHandlers()) {
 304             if (h == handler) {
 305                 found = true;
 306                 break;
 307             }
 308         }
 309 
 310         if (!found) {
 311             throw new RuntimeException("Expected handler not found in: " +
 312                     foobaz2.getName() + "(" + foobaz2.getClass().getName()+")" );
 313         }
 314 
 315         // log a message on the child logger
 316         foobaz2.severe("dummy");
 317 
 318 
 319         // check that the last published log record has the appropriate
 320         // bundle.
 321         if (!PROPERTY_BUNDLE_NAME.equals(handler.lastBundleName)) {
 322             debugLogger(foobaz2, foobar, handler);
 323             throw new RuntimeException("Unexpected bundle name: "
 324                     + handler.lastBundleName);
 325         }
 326         if (foobaz2.getResourceBundle() != handler.lastBundle) {
 327             debugLogger(foobaz2, foobar, handler);
 328             throw new RuntimeException("Unexpected bundle: "
 329                     + handler.lastBundle);
 330         }
 331 
 332         // try to set a bundle that has a different name, and checks that
 333         // it fails in IAE.
 334         try {
 335             foobaz2.setResourceBundle(bundle_fr);
 336             throw new RuntimeException("Expected exception not raised!");
 337         } catch (IllegalArgumentException x) {
 338             System.out.println("Got expected exception: " + x);
 339         }
 340 
 341         // Test with a subclass of logger which overrides
 342         // getResourceBundle() and getResourceBundleName()
 343         Logger customLogger = new Logger(foobar.getName()+".bie", null) {
 344             @Override
 345             public ResourceBundle getResourceBundle() {
 346                 return bundle_fr;
 347             }
 348 
 349             @Override
 350             public String getResourceBundleName() {
 351                 return PROPERTY_BUNDLE_NAME;
 352             }
 353         };
 354 
 355         final TestHandler handler2 = new TestHandler();
 356         customLogger.addHandler(handler2);
 357         customLogger.setLevel(Level.FINE);
 358         LogManager.getLogManager().addLogger(customLogger);
 359 
 360         Logger l = Logger.getLogger(customLogger.getName());
 361         if (l != customLogger) {
 362             throw new RuntimeException("Wrong logger: " + l);
 363         }
 364 
 365         // log on the custom logger.
 366         customLogger.fine("dummy");
 367 
 368         // check that the log record had the correct bundle.
 369         if (! PROPERTY_BUNDLE_NAME.equals(handler2.lastBundleName)) {
 370             debugLogger(customLogger, foobar, handler2);
 371             throw new RuntimeException("Unexpected bundle name: "
 372                     + handler2.lastBundleName);
 373         }
 374         if (! PROPERTY_BUNDLE_NAME.equals(customLogger.getResourceBundleName())) {
 375             debugLogger(customLogger, foobar, handler2);
 376             throw new RuntimeException("Unexpected bundle name: "
 377                     + customLogger.getResourceBundleName());
 378         }
 379         if (bundle_fr != handler2.lastBundle) {
 380             throw new RuntimeException("Unexpected bundle: "
 381                     + handler2.lastBundle);
 382         }
 383         if (bundle_fr != customLogger.getResourceBundle()) {
 384             throw new RuntimeException("Unexpected bundle: "
 385                     + customLogger.getResourceBundle());
 386         }
 387 
 388         // Do the same thing again with a child of the custom logger.
 389         Logger biebar = Logger.getLogger(customLogger.getName() + ".bar");
 390         biebar.fine("dummy");
 391 
 392         // because getResourceBundleName() is called on parent logger
 393         //         we will have handler2.lastBundleName = PROPERTY_BUNDLE_NAME
 394         if (!PROPERTY_BUNDLE_NAME.equals(handler2.lastBundleName)) {
 395             debugLogger(biebar, customLogger, handler2);
 396             throw new RuntimeException("Unexpected bundle name: "
 397                     + handler2.lastBundleName);
 398         }
 399         // because getResourceBundle() is not called on parent logger
 400         //         we will have getBaseName(handler2.lastBundle) = PROPERTY_BUNDLE_NAME
 401         //         and not handler2.lastBundle = bundle_fr
 402         if (handler2.lastBundle == null) {
 403             debugLogger(biebar, customLogger, handler2);
 404             throw new RuntimeException("Unexpected bundle: "
 405                     + handler2.lastBundle);
 406         }
 407         if (!PROPERTY_BUNDLE_NAME.equals(getBaseName(handler2.lastBundle))) {
 408             debugLogger(biebar, customLogger, handler2);
 409             throw new RuntimeException("Unexpected bundle name: "
 410                     + getBaseName(handler2.lastBundle));
 411         }
 412 
 413         // Just make sure that these loggers won't be eagerly GCed...
 414         if (foobar == null || !loggerName.equals(foobar.getName())) {
 415             throw new RuntimeException("foobar is null "
 416                     + "- or doesn't have the expected  name: " + foobar);
 417         }
 418         if (foobaz == null || !foobaz.getName().startsWith(loggerName)) {
 419             throw new RuntimeException("foobaz is null "
 420                     + "- or doesn't have the expected  name: " + foobaz);
 421         }
 422         if (foobaz2 == null || !foobaz2.getName().startsWith(loggerName)) {
 423             throw new RuntimeException("foobaz2 is null "
 424                     + "- or doesn't have the expected  name: " + foobaz2);
 425         }
 426         if (!customLogger.getName().startsWith(loggerName)) {
 427             throw new RuntimeException("customLogger "
 428                     + "doesn't have the expected name: " + customLogger);
 429         }
 430         if (!biebar.getName().startsWith(loggerName)) {
 431             throw new RuntimeException("biebar "
 432                     + "doesn't have the expected  name: " + biebar.getName());
 433         }
 434         System.out.println("Test passed for " + loggerName);
 435     }
 436 
 437     static void debugLogger(Logger logger, Logger expectedParent, TestHandler handler) {
 438         final String logName = logger.getName();
 439         final String prefix = "    " + logName;
 440         System.err.println("Logger " + logName
 441                 + " logged with bundle name " + handler.lastBundleName
 442                 + " (" + handler.lastBundle + ")");
 443         System.err.println(prefix + ".getResourceBundleName() is "
 444                 + logger.getResourceBundleName());
 445         System.err.println(prefix + ".getResourceBundle() is "
 446                 + logger.getResourceBundle());
 447         final Logger parent = logger.getParent();
 448         final String pname = parent == null ? null : parent.getName();
 449         final String pclass = parent == null ? ""
 450                 : ("(" + parent.getClass().getName() + ")");
 451         final String presn = parent == null ? null
 452                 : parent.getResourceBundleName();
 453         final ResourceBundle pres = parent == null ? null
 454                 : parent.getResourceBundle();
 455         System.err.println(prefix + ".getParent() is "
 456                 + pname + (pname == null ? ""
 457                         : (" " + pclass + ": " + parent)));
 458         System.err.println("    expected parent is :" + expectedParent);
 459         System.err.println(prefix + ".parent.getResourceBundleName() is "
 460                 + presn);
 461         System.err.println(prefix + ".parent.getResourceBundle() is "
 462                 + pres);
 463         System.err.println("    expected parent getResourceBundleName() is "
 464                 + expectedParent.getResourceBundleName());
 465         System.err.println("    expected parent.getResourceBundle() is "
 466                 + expectedParent.getResourceBundle());
 467     }
 468 
 469     public static class SimplePolicy extends Policy {
 470 
 471         final Permissions permissions;
 472         public SimplePolicy(TestCase test) {
 473             permissions = new Permissions();
 474             if (test != TestCase.PERMISSION) {
 475                 permissions.add(new LoggingPermission("control", null));
 476             }
 477             // required for calling Locale.setDefault in the test.
 478             permissions.add(new PropertyPermission("user.language", "write"));
 479         }
 480 
 481         @Override
 482         public boolean implies(ProtectionDomain domain, Permission permission) {
 483             return permissions.implies(permission) ||
 484                    DEFAULT_POLICY.implies(domain, permission);
 485         }
 486     }
 487 
 488 }