Path: blob/master/test/jdk/java/lang/System/Logger/custom/CustomLoggerTest.java
41154 views
/*1* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22import java.security.AccessControlException;23import java.security.AccessController;24import java.security.CodeSource;25import java.security.Permission;26import java.security.PermissionCollection;27import java.security.Permissions;28import java.security.Policy;29import java.security.PrivilegedAction;30import java.security.ProtectionDomain;31import java.util.Arrays;32import java.util.Collections;33import java.util.Enumeration;34import java.util.HashMap;35import java.util.Map;36import java.util.Objects;37import java.util.Queue;38import java.util.ResourceBundle;39import java.util.concurrent.ArrayBlockingQueue;40import java.util.concurrent.ConcurrentHashMap;41import java.util.concurrent.atomic.AtomicBoolean;42import java.util.concurrent.atomic.AtomicLong;43import java.util.function.Supplier;44import java.lang.System.LoggerFinder;45import java.lang.System.Logger;46import java.lang.System.Logger.Level;47import java.util.stream.Stream;48import java.security.AllPermission;4950/**51* @test52* @bug 814036453* @summary Tests loggers returned by System.getLogger with a naive implementation54* of LoggerFinder, and in particular the default body of55* System.Logger methods.56* @build CustomLoggerTest AccessSystemLogger57* @run driver AccessSystemLogger58* @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOSECURITY59* @run main/othervm -Xbootclasspath/a:boot -Djava.security.manager=allow CustomLoggerTest NOPERMISSIONS60* @run main/othervm -Xbootclasspath/a:boot -Djava.security.manager=allow CustomLoggerTest WITHPERMISSIONS61* @author danielfuchs62*/63public class CustomLoggerTest {6465final static AtomicLong sequencer = new AtomicLong();66final static boolean VERBOSE = false;67static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {68@Override69protected AtomicBoolean initialValue() {70return new AtomicBoolean(false);71}72};73static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {74@Override75protected AtomicBoolean initialValue() {76return new AtomicBoolean(false);77}78};7980public static class MyBundle extends ResourceBundle {8182final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();8384@Override85protected Object handleGetObject(String key) {86if (key.contains(" (translated)")) {87throw new RuntimeException("Unexpected key: " + key);88}89return map.computeIfAbsent(key, k -> k + " (translated)");90}9192@Override93public Enumeration<String> getKeys() {94return Collections.enumeration(map.keySet());95}9697}98public static class MyLoggerBundle extends MyBundle {99100}101102103public static class BaseLoggerFinder extends LoggerFinder {104final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();105final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();106public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);107108// changing this to true requires changing the logic in the109// test in order to load this class with a protection domain110// that has the CONTROL_PERMISSION (e.g. by using a custom111// system class loader.112final boolean doChecks = false;113114public static final class LogEvent {115116public LogEvent() {117this(sequencer.getAndIncrement());118}119120LogEvent(long sequenceNumber) {121this.sequenceNumber = sequenceNumber;122}123124long sequenceNumber;125boolean isLoggable;126String loggerName;127Level level;128ResourceBundle bundle;129Throwable thrown;130Object[] args;131Supplier<String> supplier;132String msg;133134Object[] toArray() {135return new Object[] {136sequenceNumber,137isLoggable,138loggerName,139level,140bundle,141thrown,142args,143supplier,144msg,145};146}147148@Override149public String toString() {150return Arrays.deepToString(toArray());151}152153154155@Override156public boolean equals(Object obj) {157return obj instanceof LogEvent158&& Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());159}160161@Override162public int hashCode() {163return Objects.hash(toArray());164}165166167public static LogEvent of(boolean isLoggable, String name,168Level level, ResourceBundle bundle,169String key, Throwable thrown) {170LogEvent evt = new LogEvent();171evt.isLoggable = isLoggable;172evt.loggerName = name;173evt.level = level;174evt.args = null;175evt.bundle = bundle;176evt.thrown = thrown;177evt.supplier = null;178evt.msg = key;179return evt;180}181182public static LogEvent of(boolean isLoggable, String name,183Level level, ResourceBundle bundle,184String key, Object... params) {185LogEvent evt = new LogEvent();186evt.isLoggable = isLoggable;187evt.loggerName = name;188evt.level = level;189evt.args = params;190evt.bundle = bundle;191evt.thrown = null;192evt.supplier = null;193evt.msg = key;194return evt;195}196197public static LogEvent of(long sequenceNumber,198boolean isLoggable, String name,199Level level, ResourceBundle bundle,200String key, Supplier<String> supplier,201Throwable thrown, Object... params) {202LogEvent evt = new LogEvent(sequenceNumber);203evt.loggerName = name;204evt.level = level;205evt.args = params;206evt.bundle = bundle;207evt.thrown = thrown;208evt.supplier = supplier;209evt.msg = key;210evt.isLoggable = isLoggable;211return evt;212}213214}215216public class LoggerImpl implements Logger {217private final String name;218private Level level = Level.INFO;219220public LoggerImpl(String name) {221this.name = name;222}223224@Override225public String getName() {226return name;227}228229@Override230public boolean isLoggable(Level level) {231return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();232}233234@Override235public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {236log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));237}238239@Override240public void log(Level level, ResourceBundle bundle, String format, Object... params) {241log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));242}243244void log(LogEvent event) {245eventQueue.add(event);246}247}248249@Override250public Logger getLogger(String name, Module caller) {251// We should check the permission to obey the API contract, but252// what happens if we don't?253// This is the main difference compared with what we test in254// java/lang/System/LoggerFinder/BaseLoggerFinderTest255SecurityManager sm = System.getSecurityManager();256if (sm != null && doChecks) {257sm.checkPermission(SimplePolicy.LOGGERFINDER_PERMISSION);258}259260final boolean before = allowAll.get().getAndSet(true);261final ClassLoader callerLoader;262try {263callerLoader = caller.getClassLoader();264} finally {265allowAll.get().set(before);266}267if (callerLoader == null) {268return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));269} else {270return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));271}272}273}274275static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();276277static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};278279static void setSecurityManager() {280if (System.getSecurityManager() == null) {281Policy.setPolicy(new SimplePolicy(allowControl, allowAll));282System.setSecurityManager(new SecurityManager());283}284}285public static void main(String[] args) {286if (args.length == 0)287args = new String[] {288"NOSECURITY",289"NOPERMISSIONS",290"WITHPERMISSIONS"291};292293// 1. Obtain destination loggers directly from the LoggerFinder294// - LoggerFinder.getLogger("foo", type)295BaseLoggerFinder provider =296BaseLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());297BaseLoggerFinder.LoggerImpl appSink =298BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", CustomLoggerTest.class.getModule()));299BaseLoggerFinder.LoggerImpl sysSink =300BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class.getModule()));301302303Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {304switch (testCase) {305case NOSECURITY:306System.out.println("\n*** Without Security Manager\n");307test(provider, true, appSink, sysSink);308System.out.println("Tetscase count: " + sequencer.get());309break;310case NOPERMISSIONS:311System.out.println("\n*** With Security Manager, without permissions\n");312setSecurityManager();313test(provider, false, appSink, sysSink);314System.out.println("Tetscase count: " + sequencer.get());315break;316case WITHPERMISSIONS:317System.out.println("\n*** With Security Manager, with control permission\n");318setSecurityManager();319final boolean control = allowControl.get().get();320try {321allowControl.get().set(true);322test(provider, true, appSink, sysSink);323} finally {324allowControl.get().set(control);325}326break;327default:328throw new RuntimeException("Unknown test case: " + testCase);329}330});331System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");332}333334public static void test(BaseLoggerFinder provider, boolean hasRequiredPermissions,335BaseLoggerFinder.LoggerImpl appSink, BaseLoggerFinder.LoggerImpl sysSink) {336337ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());338final Map<Logger, String> loggerDescMap = new HashMap<>();339340341// 1. Test loggers returned by:342// - System.getLogger("foo")343// - and AccessSystemLogger.getLogger("foo")344Logger appLogger1 = System.getLogger("foo");345loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");346347Logger sysLogger1 = null;348try {349sysLogger1 = accessSystemLogger.getLogger("foo");350loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");351} catch (AccessControlException acx) {352if (hasRequiredPermissions) {353throw new RuntimeException("Unexpected security exception: ", acx);354}355if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {356throw new RuntimeException("Unexpected permission in exception: " + acx, acx);357}358throw new RuntimeException("unexpected exception: " + acx, acx);359}360361if (appLogger1 == sysLogger1) {362throw new RuntimeException("identical loggers");363}364365if (provider.system.contains(appLogger1)) {366throw new RuntimeException("app logger in system map");367}368if (provider.user.contains(sysLogger1)) {369throw new RuntimeException("sys logger in appplication map");370}371if (provider.system.contains(sysLogger1)) {372// sysLogger should be a a LazyLoggerWrapper373throw new RuntimeException("sys logger is in system map (should be wrapped)");374}375376377// 2. Test loggers returned by:378// - System.getLogger(\"foo\", loggerBundle)379// - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)380Logger appLogger2 =381System.getLogger("foo", loggerBundle);382loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");383384Logger sysLogger2 = null;385try {386sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);387loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");388} catch (AccessControlException acx) {389if (hasRequiredPermissions) {390throw new RuntimeException("Unexpected security exception: ", acx);391}392if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {393throw new RuntimeException("Unexpected permission in exception: " + acx, acx);394}395throw new RuntimeException("unexpected exception: " + acx, acx);396}397if (appLogger2 == sysLogger2) {398throw new RuntimeException("identical loggers");399}400if (appLogger2 == appSink) {401throw new RuntimeException("identical loggers");402}403if (sysLogger2 == sysSink) {404throw new RuntimeException("identical loggers");405}406407if (provider.system.contains(appLogger2)) {408throw new RuntimeException("localized app logger in system map");409}410if (provider.user.contains(appLogger2)) {411throw new RuntimeException("localized app logger in appplication map");412}413if (provider.user.contains(sysLogger2)) {414throw new RuntimeException("localized sys logger in appplication map");415}416if (provider.system.contains(sysLogger2)) {417throw new RuntimeException("localized sys logger not in system map");418}419420testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);421testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);422testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);423testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);424}425426public static class Foo {427428}429430static void verbose(String msg) {431if (VERBOSE) {432System.out.println(msg);433}434}435436// Calls the 8 methods defined on Logger and verify the437// parameters received by the underlying BaseLoggerFinder.LoggerImpl438// logger.439private static void testLogger(BaseLoggerFinder provider,440Map<Logger, String> loggerDescMap,441String name,442ResourceBundle loggerBundle,443Logger logger,444BaseLoggerFinder.LoggerImpl sink) {445446System.out.println("Testing " + loggerDescMap.get(logger));447448Foo foo = new Foo();449String fooMsg = foo.toString();450for (Level loggerLevel : Level.values()) {451sink.level = loggerLevel;452for (Level messageLevel : Level.values()) {453String desc = "logger.log(messageLevel, foo): loggerLevel="454+ loggerLevel+", messageLevel="+messageLevel;455BaseLoggerFinder.LogEvent expected =456BaseLoggerFinder.LogEvent.of(457sequencer.get(),458messageLevel.compareTo(loggerLevel) >= 0,459name, messageLevel, (ResourceBundle)null,460fooMsg, null, (Throwable)null, (Object[])null);461logger.log(messageLevel, foo);462if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {463if (provider.eventQueue.poll() != null) {464throw new RuntimeException("unexpected event in queue for " + desc);465}466} else {467BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();468if (!expected.equals(actual)) {469throw new RuntimeException("mismatch for " + desc470+ "\n\texpected=" + expected471+ "\n\t actual=" + actual);472} else {473verbose("Got expected results for "474+ desc + "\n\t" + expected);475}476}477}478}479480String msg = "blah";481for (Level loggerLevel : Level.values()) {482sink.level = loggerLevel;483for (Level messageLevel : Level.values()) {484String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="485+ loggerLevel+", messageLevel="+messageLevel;486BaseLoggerFinder.LogEvent expected =487BaseLoggerFinder.LogEvent.of(488sequencer.get(),489messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,490name, messageLevel, loggerBundle,491msg, null, (Throwable)null, (Object[])null);492logger.log(messageLevel, msg);493BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();494if (!expected.equals(actual)) {495throw new RuntimeException("mismatch for " + desc496+ "\n\texpected=" + expected497+ "\n\t actual=" + actual);498} else {499verbose("Got expected results for "500+ desc + "\n\t" + expected);501}502}503}504505Supplier<String> fooSupplier = new Supplier<String>() {506@Override507public String get() {508return this.toString();509}510};511512for (Level loggerLevel : Level.values()) {513sink.level = loggerLevel;514for (Level messageLevel : Level.values()) {515String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="516+ loggerLevel+", messageLevel="+messageLevel;517BaseLoggerFinder.LogEvent expected =518BaseLoggerFinder.LogEvent.of(519sequencer.get(),520messageLevel.compareTo(loggerLevel) >= 0,521name, messageLevel, (ResourceBundle)null,522fooSupplier.get(), null,523(Throwable)null, (Object[])null);524logger.log(messageLevel, fooSupplier);525if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {526if (provider.eventQueue.poll() != null) {527throw new RuntimeException("unexpected event in queue for " + desc);528}529} else {530BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();531if (!expected.equals(actual)) {532throw new RuntimeException("mismatch for " + desc533+ "\n\texpected=" + expected534+ "\n\t actual=" + actual);535} else {536verbose("Got expected results for "537+ desc + "\n\t" + expected);538}539}540}541}542543String format = "two params [{1} {2}]";544Object arg1 = foo;545Object arg2 = msg;546for (Level loggerLevel : Level.values()) {547sink.level = loggerLevel;548for (Level messageLevel : Level.values()) {549String desc = "logger.log(messageLevel, format, params...): loggerLevel="550+ loggerLevel+", messageLevel="+messageLevel;551BaseLoggerFinder.LogEvent expected =552BaseLoggerFinder.LogEvent.of(553sequencer.get(),554messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,555name, messageLevel, loggerBundle,556format, null, (Throwable)null, new Object[] {arg1, arg2});557logger.log(messageLevel, format, arg1, arg2);558BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();559if (!expected.equals(actual)) {560throw new RuntimeException("mismatch for " + desc561+ "\n\texpected=" + expected562+ "\n\t actual=" + actual);563} else {564verbose("Got expected results for "565+ desc + "\n\t" + expected);566}567}568}569570Throwable thrown = new Exception("OK: log me!");571for (Level loggerLevel : Level.values()) {572sink.level = loggerLevel;573for (Level messageLevel : Level.values()) {574String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="575+ loggerLevel+", messageLevel="+messageLevel;576BaseLoggerFinder.LogEvent expected =577BaseLoggerFinder.LogEvent.of(578sequencer.get(),579messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,580name, messageLevel, loggerBundle,581msg, null, thrown, (Object[]) null);582logger.log(messageLevel, msg, thrown);583BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();584if (!expected.equals(actual)) {585throw new RuntimeException("mismatch for " + desc586+ "\n\texpected=" + expected587+ "\n\t actual=" + actual);588} else {589verbose("Got expected results for "590+ desc + "\n\t" + expected);591}592}593}594595596for (Level loggerLevel : Level.values()) {597sink.level = loggerLevel;598for (Level messageLevel : Level.values()) {599String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="600+ loggerLevel+", messageLevel="+messageLevel;601BaseLoggerFinder.LogEvent expected =602BaseLoggerFinder.LogEvent.of(603sequencer.get(),604messageLevel.compareTo(loggerLevel) >= 0,605name, messageLevel, (ResourceBundle)null,606fooSupplier.get(), null,607(Throwable)thrown, (Object[])null);608logger.log(messageLevel, fooSupplier, thrown);609if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {610if (provider.eventQueue.poll() != null) {611throw new RuntimeException("unexpected event in queue for " + desc);612}613} else {614BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();615if (!expected.equals(actual)) {616throw new RuntimeException("mismatch for " + desc617+ "\n\texpected=" + expected618+ "\n\t actual=" + actual);619} else {620verbose("Got expected results for "621+ desc + "\n\t" + expected);622}623}624}625}626627ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());628for (Level loggerLevel : Level.values()) {629sink.level = loggerLevel;630for (Level messageLevel : Level.values()) {631String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="632+ loggerLevel+", messageLevel="+messageLevel;633BaseLoggerFinder.LogEvent expected =634BaseLoggerFinder.LogEvent.of(635sequencer.get(),636messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,637name, messageLevel, bundle,638format, null, (Throwable)null, new Object[] {foo, msg});639logger.log(messageLevel, bundle, format, foo, msg);640BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();641if (!expected.equals(actual)) {642throw new RuntimeException("mismatch for " + desc643+ "\n\texpected=" + expected644+ "\n\t actual=" + actual);645} else {646verbose("Got expected results for "647+ desc + "\n\t" + expected);648}649}650}651652for (Level loggerLevel : Level.values()) {653sink.level = loggerLevel;654for (Level messageLevel : Level.values()) {655String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="656+ loggerLevel+", messageLevel="+messageLevel;657BaseLoggerFinder.LogEvent expected =658BaseLoggerFinder.LogEvent.of(659sequencer.get(),660messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,661name, messageLevel, bundle,662msg, null, thrown, (Object[]) null);663logger.log(messageLevel, bundle, msg, thrown);664BaseLoggerFinder.LogEvent actual = provider.eventQueue.poll();665if (!expected.equals(actual)) {666throw new RuntimeException("mismatch for " + desc667+ "\n\texpected=" + expected668+ "\n\t actual=" + actual);669} else {670verbose("Got expected results for "671+ desc + "\n\t" + expected);672}673}674}675}676677final static class PermissionsBuilder {678final Permissions perms;679public PermissionsBuilder() {680this(new Permissions());681}682public PermissionsBuilder(Permissions perms) {683this.perms = perms;684}685public PermissionsBuilder add(Permission p) {686perms.add(p);687return this;688}689public PermissionsBuilder addAll(PermissionCollection col) {690if (col != null) {691for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {692perms.add(e.nextElement());693}694}695return this;696}697public Permissions toPermissions() {698final PermissionsBuilder builder = new PermissionsBuilder();699builder.addAll(perms);700return builder.perms;701}702}703704public static class SimplePolicy extends Policy {705706static final Policy DEFAULT_POLICY = Policy.getPolicy();707708static final RuntimePermission LOGGERFINDER_PERMISSION =709new RuntimePermission("loggerFinder");710final Permissions permissions;711final Permissions controlPermissions;712final Permissions allPermissions;713final ThreadLocal<AtomicBoolean> allowControl;714final ThreadLocal<AtomicBoolean> allowAll;715public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAll) {716this.allowControl = allowControl;717this.allowAll = allowAll;718permissions = new Permissions();719720// these are used for configuring the test itself...721controlPermissions = new Permissions();722controlPermissions.add(LOGGERFINDER_PERMISSION);723724// these are used for simulating a doPrivileged call from725// a class in the BCL726allPermissions = new Permissions();727allPermissions.add(new AllPermission());728729}730731Permissions permissions() {732if (allowAll.get().get()) return allPermissions;733if (allowControl.get().get()) return controlPermissions;734return permissions;735736}737738@Override739public boolean implies(ProtectionDomain domain, Permission permission) {740return permissions().implies(permission) || DEFAULT_POLICY.implies(domain, permission);741}742743@Override744public PermissionCollection getPermissions(CodeSource codesource) {745return new PermissionsBuilder().addAll(permissions()).toPermissions();746}747748@Override749public PermissionCollection getPermissions(ProtectionDomain domain) {750return new PermissionsBuilder().addAll(permissions()).toPermissions();751}752}753}754755756