Index: core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java =================================================================== --- core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java (revision 0) +++ core/src/test/java/org/apache/logging/log4j/LocationPerfCheck.java (revision 0) @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.Timer; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.message.SimpleMessage; +import org.junit.Test; + +/** + * + */ +public class LocationPerfCheck { + + private static final int LOOP_COUNT = 100000; + + + @Test + public void testLocation() { + Timer timer = new Timer("LogEvent", LOOP_COUNT); + timer.start(); + for (int i = 0; i < LOOP_COUNT; ++i) { + final LogEvent event1 = new Log4jLogEvent(this.getClass().getName(), null, + "org.apache.logging.log4j.core.impl.Log4jLogEvent", + Level.INFO, new SimpleMessage("Hello, world!"), null); + StackTraceElement element = event1.getSource(); + } + timer.stop(); + System.out.println(timer.toString()); + } +} Index: core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java =================================================================== --- core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java (revision 1439209) +++ core/src/test/java/org/apache/logging/log4j/core/appender/rewrite/MapRewritePolicyTest.java (working copy) @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.core.appender.rewrite; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -29,6 +30,8 @@ import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.message.StructuredDataMessage; +import org.apache.logging.log4j.spi.MutableThreadContextStack; +import org.apache.logging.log4j.spi.ThreadContextStack; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -49,12 +52,13 @@ logEvent1 = new Log4jLogEvent("test", null, "MapRewritePolicyTest.setupClass()", Level.ERROR, new MapMessage(map), null, map, null, "none", new StackTraceElement("MapRewritePolicyTest", "setupClass", "MapRewritePolicyTest", 29), 2); + ThreadContextStack stack = new MutableThreadContextStack(new ArrayList(map.values())); logEvent2 = new Log4jLogEvent("test", MarkerManager.getMarker("test"), "MapRewritePolicyTest.setupClass()", Level.TRACE, new StructuredDataMessage("test", "Nothing", "test", map), new RuntimeException("test"), null, - new ThreadContext.ImmutableStack(map.values()), "none", new StackTraceElement("MapRewritePolicyTest", + stack, "none", new StackTraceElement("MapRewritePolicyTest", "setupClass", "MapRewritePolicyTest", 30), 20000000); logEvent3 = new Log4jLogEvent("test", null, "MapRewritePolicyTest.setupClass()", Level.ALL, new MapMessage(map), - null, map, new ThreadContext.ImmutableStack(map.values()), null, new StackTraceElement("MapRewritePolicyTest", + null, map, stack, null, new StackTraceElement("MapRewritePolicyTest", "setupClass", "MapRewritePolicyTest", 31), Long.MAX_VALUE); rewrite = new KeyValuePair[] {new KeyValuePair("test2", "2"), new KeyValuePair("test3", "three")}; } Index: core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java =================================================================== --- core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java (revision 1439209) +++ core/src/main/java/org/apache/logging/log4j/core/impl/Log4jLogEvent.java (working copy) @@ -28,6 +28,8 @@ import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -123,14 +125,14 @@ if (properties == null || properties.size() == 0) { return ThreadContext.getImmutableContext(); } - final Map map = ThreadContext.getContext(); + final Map map = new HashMap(ThreadContext.getContext()); for (final Property prop : properties) { if (!map.containsKey(prop.getName())) { map.put(prop.getName(), prop.getValue()); } } - return new ThreadContext.ImmutableMap(map); + return Collections.unmodifiableMap(map); } /** Index: api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java =================================================================== --- api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java (revision 1439209) +++ api/src/test/java/org/apache/logging/log4j/ThreadContextTest.java (working copy) @@ -18,6 +18,8 @@ import org.junit.Test; +import java.util.Map; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -52,7 +54,34 @@ assertTrue("Unexpected ThreadContext value. Expected Hello. Actual " + str, "Hello".equals(str)); } + @Test + public void perfTest() throws Exception { + ThreadContext.clear(); + Timer complete = new Timer("ThreadContextTest"); + complete.start(); + ThreadContext.put("Var1", "value 1"); + ThreadContext.put("Var2", "value 2"); + ThreadContext.put("Var3", "value 3"); + ThreadContext.put("Var4", "value 4"); + ThreadContext.put("Var5", "value 5"); + ThreadContext.put("Var6", "value 6"); + ThreadContext.put("Var7", "value 7"); + ThreadContext.put("Var8", "value 8"); + ThreadContext.put("Var9", "value 9"); + ThreadContext.put("Var10", "value 10"); + final int loopCount = 1000000; + Timer timer = new Timer("ThreadContextCopy", loopCount); + timer.start(); + for (int i = 0; i < loopCount; ++i) { + Map map = ThreadContext.getImmutableContext(); + } + timer.stop(); + complete.stop(); + System.out.println(timer.toString()); + System.out.println(complete.toString()); + } + private class TestThread extends Thread { private final StringBuilder sb; Index: api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java =================================================================== --- api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java (revision 1439209) +++ api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java (working copy) @@ -16,11 +16,17 @@ */ package org.apache.logging.log4j.spi; +import org.apache.logging.log4j.ThreadContext; + +import java.util.Collections; import java.util.HashMap; import java.util.Map; /** - * + * The actual ThreadContext Map. A new ThreadContext Map is created each time it is updated and the Map stored + * is always immutable. This means the Map can be passed to other threads without concern that it will be updated. + * Since it is expected that the Map will be passed to many more log events than the number of keys it contains + * the performance should be much better than if the Map was copied for each event. */ public class DefaultThreadContextMap implements ThreadContextMap { @@ -30,7 +36,8 @@ new InheritableThreadLocal>() { @Override protected Map childValue(final Map parentValue) { - return parentValue == null || !useMap ? null : new HashMap(parentValue); + return parentValue == null || !useMap ? null : + Collections.unmodifiableMap(new HashMap(parentValue)); } }; @@ -53,11 +60,9 @@ return; } Map map = localMap.get(); - if (map == null) { - map = new HashMap(); - localMap.set(map); - } + map = map == null ? new HashMap() : new HashMap(map); map.put(key, value); + localMap.set(Collections.unmodifiableMap(map)); } /** @@ -78,9 +83,11 @@ * @param key The key to remove. */ public void remove(final String key) { - final Map map = localMap.get(); + Map map = localMap.get(); if (map != null) { + map = new HashMap(map); map.remove(key); + localMap.set(Collections.unmodifiableMap(map)); } } @@ -98,16 +105,16 @@ */ public boolean containsKey(final String key) { final Map map = localMap.get(); - return map == null ? false : map.containsKey(key); + return map != null && map.containsKey(key); } /** - * Get a copy of current thread's context Map. + * Return an immutable copy of the ThreadContext Map. * @return a copy of the context. */ public Map getContext() { final Map map = localMap.get(); - return map == null ? new HashMap() : new HashMap(map); + return map == null ? ThreadContext.EMPTY_MAP : map; } /** Index: api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java =================================================================== --- api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java (revision 0) +++ api/src/main/java/org/apache/logging/log4j/spi/ThreadContextStack.java (revision 0) @@ -0,0 +1,13 @@ +package org.apache.logging.log4j.spi; + +import org.apache.logging.log4j.ThreadContext; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + * + */ +public interface ThreadContextStack extends ThreadContext.ContextStack, Collection, Serializable { +} Index: api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java =================================================================== --- api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java (revision 0) +++ api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextStack.java (revision 0) @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.spi; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * + */ +public class DefaultThreadContextStack implements ThreadContextStack { + + private static final long serialVersionUID = 5050501L; + + private final boolean useStack; + + private static ThreadLocal> stack = new ThreadLocal>(); + + public DefaultThreadContextStack(boolean useStack) { + this.useStack = useStack; + } + + public String pop() { + if (!useStack) { + return ""; + } + List list = stack.get(); + if (list == null || list.size() == 0) { + throw new NoSuchElementException("The ThreadContext stack is empty"); + } + list = new ArrayList(list); + final int index = list.size() - 1; + final String result = list.get(index); + list.remove(index); + stack.set(Collections.unmodifiableList(list)); + return result; + } + + public String peek() { + List list = stack.get(); + if (list == null || list.size() == 0) { + return null; + } + final int index = list.size() - 1; + return list.get(index); + } + + public void push(final String message) { + if (!useStack) { + return; + } + add(message); + } + + public int getDepth() { + List list = stack.get(); + if (list == null) { + return 0; + } + return list.size(); + } + + public List asList() { + List list = stack.get(); + return list == null ? Collections.unmodifiableList(new ArrayList()) : list; + } + + public void trim(final int depth) { + if (depth < 0) { + throw new IllegalArgumentException("Maximum stack depth cannot be negative"); + } + while (size() > depth) { + remove(size() - 1); + } + + } + + public ThreadContextStack copy() { + if (!useStack || stack.get() == null) { + return new MutableThreadContextStack(new ArrayList()); + } + return new MutableThreadContextStack(stack.get()); + } + + public void clear() { + stack.remove(); + } + + public int size() { + return stack.get() == null ? 0 : stack.get().size(); + } + + public boolean isEmpty() { + return stack.get() == null || stack.get().isEmpty(); + } + + public boolean contains(Object o) { + return stack.get() != null && stack.get().contains(o); + } + + public Iterator iterator() { + if (stack.get() == null) { + return new ArrayList().iterator(); + } else { + return stack.get().iterator(); + } + } + + public Object[] toArray() { + if (stack.get() == null) { + return new String[0]; + } else { + List list = stack.get(); + return stack.get().toArray(new Object[list.size()]); + } + } + + public T[] toArray(T[] ts) { + if (stack.get() == null) { + return new ArrayList().toArray(ts); + } + return stack.get().toArray(ts); + } + + public boolean add(String s) { + if (!useStack) { + return false; + } + List list = stack.get(); + List newList = list == null ? new ArrayList() : new ArrayList(list); + newList.add(s); + stack.set(Collections.unmodifiableList(newList)); + return true; + } + + public boolean remove(Object o) { + if (!useStack) { + return false; + } + List list = stack.get(); + if (list == null || list.size() == 0) { + return false; + } + list = new ArrayList(list); + boolean result = list.remove(o); + stack.set(Collections.unmodifiableList(list)); + return result; + } + + public boolean containsAll(Collection objects) { + List list = stack.get(); + return list != null && list.containsAll(objects); + } + + public boolean addAll(Collection strings) { + if (!useStack) { + return false; + } + List list = stack.get(); + List newList = list == null ? new ArrayList() : new ArrayList(list); + newList.addAll(strings); + stack.set(Collections.unmodifiableList(newList)); + return true; + } + + public boolean removeAll(Collection objects) { + if (!useStack) { + return false; + } + List list = stack.get(); + if (list == null || list.size() == 0) { + return false; + } + list = new ArrayList(list); + boolean result = list.removeAll(objects); + stack.set(Collections.unmodifiableList(list)); + return result; + } + + public boolean retainAll(Collection objects) { + if (!useStack) { + return false; + } + List list = stack.get(); + if (list == null || list.size() == 0) { + return false; + } + list = new ArrayList(list); + boolean result = list.retainAll(objects); + stack.set(Collections.unmodifiableList(list)); + return result; + } +} Index: api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java =================================================================== --- api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java (revision 0) +++ api/src/main/java/org/apache/logging/log4j/spi/MutableThreadContextStack.java (revision 0) @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.spi; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * + */ +public class MutableThreadContextStack implements ThreadContextStack { + + private static final long serialVersionUID = 50505011L; + + private final List list; + + public MutableThreadContextStack(List list) { + this.list = new ArrayList(list); + } + + private MutableThreadContextStack(MutableThreadContextStack stack) { + this.list = new ArrayList(stack.list); + } + + public String pop() { + if (list.size() == 0) { + return null; + } + final int index = list.size() - 1; + final String result = list.get(index); + list.remove(index); + return result; + } + + public String peek() { + if (list == null || list.size() == 0) { + return null; + } + final int index = list.size() - 1; + return list.get(index); + } + + public void push(final String message) { + list.add(message); + } + + public int getDepth() { + if (list == null) { + return 0; + } + return list.size(); + } + + public List asList() { + return list == null ? Collections.unmodifiableList(new ArrayList()) : list; + } + + public void trim(final int depth) { + if (depth < 0) { + throw new IllegalArgumentException("Maximum stack depth cannot be negative"); + } + while (size() > depth) { + remove(size() - 1); + } + + } + + public ThreadContextStack copy() { + return new MutableThreadContextStack(this); + } + + public void clear() { + list.clear(); + } + + public int size() { + return list.size(); + } + + public boolean isEmpty() { + return list.isEmpty(); + } + + public boolean contains(Object o) { + return list.contains(o); + } + + public Iterator iterator() { + return list.iterator(); + } + + public Object[] toArray() { + return list.toArray(); + } + + public T[] toArray(T[] ts) { + return list.toArray(ts); + } + + public boolean add(String s) { + return list.add(s); + } + + public boolean remove(Object o) { + return list.remove(o); + } + + public boolean containsAll(Collection objects) { + return list.containsAll(objects); + } + + public boolean addAll(Collection strings) { + return list.addAll(strings); + } + + public boolean removeAll(Collection objects) { + if (list.size() == 0) { + return false; + } + return list.removeAll(objects); + } + + public boolean retainAll(Collection objects) { + return list.size() != 0 && list.retainAll(objects); + } +} Index: api/src/main/java/org/apache/logging/log4j/ThreadContext.java =================================================================== --- api/src/main/java/org/apache/logging/log4j/ThreadContext.java (revision 1439209) +++ api/src/main/java/org/apache/logging/log4j/ThreadContext.java (working copy) @@ -19,9 +19,12 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.spi.DefaultThreadContextMap; +import org.apache.logging.log4j.spi.DefaultThreadContextStack; import org.apache.logging.log4j.spi.LoggerContextFactory; +import org.apache.logging.log4j.spi.MutableThreadContextStack; import org.apache.logging.log4j.spi.Provider; import org.apache.logging.log4j.spi.ThreadContextMap; +import org.apache.logging.log4j.spi.ThreadContextStack; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.PropertiesUtil; import org.apache.logging.log4j.util.ProviderUtil; @@ -29,6 +32,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -47,12 +51,12 @@ /** * Empty, immutable Map. */ - public static final Map EMPTY_MAP = new ImmutableMap(); + public static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap()); /** * Empty, immutable ContextStack. */ - public static final ContextStack EMPTY_STACK = new ImmutableStack(); + public static final ThreadContextStack EMPTY_STACK = new MutableThreadContextStack(new ArrayList()); private static final String DISABLE_MAP = "disableThreadContextMap"; @@ -70,6 +74,8 @@ private static ThreadContextMap contextMap; + private static ThreadContextStack contextStack; + private static final Logger LOGGER = StatusLogger.getLogger(); static { @@ -121,10 +127,9 @@ } else { contextMap = new DefaultThreadContextMap(useMap); } + contextStack = new DefaultThreadContextStack(useStack); } - private static ThreadLocal localStack = new ThreadLocal(); - private ThreadContext() { } @@ -193,7 +198,7 @@ */ public static Map getImmutableContext() { final Map map = contextMap.get(); - return map == null ? new ImmutableMap() : new ImmutableMap(map); + return map == null ? EMPTY_MAP : Collections.unmodifiableMap(map); } /** @@ -208,7 +213,7 @@ * Clear the stack for this thread. */ public static void clearStack() { - localStack.remove(); + contextStack.clear(); } /** @@ -216,8 +221,7 @@ * @return A copy of this thread's stack. */ public static ContextStack cloneStack() { - final ContextStack stack = localStack.get(); - return stack == null ? new ThreadContextStack() : new ThreadContextStack(stack.asList()); + return contextStack.copy(); } /** @@ -225,8 +229,7 @@ * @return an immutable copy of the ThreadContext stack. */ public static ContextStack getImmutableStack() { - final ContextStack stack = localStack.get(); - return stack == null ? EMPTY_STACK : new ImmutableStack(stack.asList()); + return contextStack; } /** @@ -237,7 +240,8 @@ if (stack.size() == 0 || !useStack) { return; } - localStack.set(new ThreadContextStack(stack)); + contextStack.clear(); + contextStack.addAll(stack); } /** @@ -247,8 +251,7 @@ * @see #trim */ public static int getDepth() { - final ContextStack stack = localStack.get(); - return stack == null ? 0 : stack.getDepth(); + return contextStack.getDepth(); } /** @@ -260,11 +263,7 @@ * @return String The innermost diagnostic context. */ public static String pop() { - final ContextStack s = localStack.get(); - if (s == null || s.getDepth() == 0) { - return ""; - } - return s.pop(); + return contextStack.pop(); } /** @@ -277,11 +276,7 @@ * @return String The innermost diagnostic context. */ public static String peek() { - final ContextStack s = localStack.get(); - if (s == null || s.getDepth() == 0) { - return ""; - } - return s.peek(); + return contextStack.peek(); } /** @@ -293,15 +288,7 @@ * @param message The new diagnostic context information. */ public static void push(final String message) { - if (!useStack) { - return; - } - ContextStack stack = localStack.get(); - if (stack == null) { - stack = new ThreadContextStack(); - localStack.set(stack); - } - stack.push(message); + contextStack.push(message); } /** * Push new diagnostic context information for the current thread. @@ -315,15 +302,7 @@ * @param args Parameters for the message. */ public static void push(final String message, final Object... args) { - if (!useStack) { - return; - } - ContextStack stack = localStack.get(); - if (stack == null) { - stack = new ThreadContextStack(); - localStack.set(stack); - } - stack.push(ParameterizedMessage.format(message, args)); + contextStack.push(ParameterizedMessage.format(message, args)); } /** @@ -345,7 +324,7 @@ * memory. */ public static void removeStack() { - localStack.remove(); + contextStack.clear(); } /** @@ -378,10 +357,7 @@ * @param depth The number of elements to keep. */ public static void trim(final int depth) { - final ContextStack stack = localStack.get(); - if (stack != null) { - stack.trim(depth); - } + contextStack.trim(depth); } /** @@ -437,128 +413,4 @@ */ ContextStack copy(); } - - /** - * The ContextStack implementation. - */ - private static class ThreadContextStack extends ArrayList implements ContextStack { - - private static final long serialVersionUID = 5050501L; - - public ThreadContextStack() { - super(); - } - - public ThreadContextStack(final Collection collection) { - super(collection); - } - - public String pop() { - final int index = size() - 1; - if (index >= 0) { - final String result = get(index); - remove(index); - return result; - } - throw new NoSuchElementException("The ThreadContext stack is empty"); - } - - public String peek() { - final int index = size() - 1; - if (index >= 0) { - return get(index); - } - return null; - } - - public void push(final String message) { - add(message); - } - - public int getDepth() { - return size(); - } - - public List asList() { - return this; - } - - public void trim(final int depth) { - if (depth < 0) { - throw new IllegalArgumentException("Maximum stack depth cannot be negative"); - } - while (size() > depth) { - remove(size() - 1); - } - - } - - public ContextStack copy() { - return new ThreadContextStack(this); - } - } - - /** - * An immutable ContextStack. - */ - public static class ImmutableStack extends ThreadContextStack { - - private static final long serialVersionUID = 5050502L; - - public ImmutableStack() { - } - - public ImmutableStack(final Collection collection) { - super(collection); - } - - public ImmutableStack(final ThreadContextStack stack) { - super(stack); - } - - @Override - public void push(final String message) { - throw new UnsupportedOperationException("Stack cannot be modified"); - } - - @Override - public void trim(final int depth) { - throw new UnsupportedOperationException("Stack cannot be modified"); - } - } - - /** - * An immutable Context Map. - */ - public static class ImmutableMap extends HashMap { - private static final long serialVersionUID = 5050503L; - - public ImmutableMap() { - super(); - } - - public ImmutableMap(final Map map) { - super(map); - } - - @Override - public String put(final String s, final String s1) { - throw new UnsupportedOperationException("Map cannot be modified"); - } - - @Override - public void putAll(final Map map) { - throw new UnsupportedOperationException("Map cannot be modified"); - } - - @Override - public String remove(final Object o) { - throw new UnsupportedOperationException("Map cannot be modified"); - } - - @Override - public void clear() { - throw new UnsupportedOperationException("Map cannot be modified"); - } - } }