/* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software License * version 1.1, a copy of which has been included with this distribution in * the LICENSE file. */ package org.jivesoftware.util.log; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * The ContextMap contains non-hierarchical context information * relevent to a particular LogEvent. It may include information * such as; * <p/> * <ul> * <li>user ->fred</li> * <li>hostname ->helm.realityforge.org</li> * <li>ipaddress ->1.2.3.4</li> * <li>interface ->127.0.0.1</li> * <li>caller � �->com.biz.MyCaller.method(MyCaller.java:18)</li> * <li>source � �->1.6.3.2:33</li> * </ul> * The context is bound to a thread (and inherited by sub-threads) but * it can also be added to by LogTargets. * * @author <a href="mailto:peter@apache.org">Peter Donald</a> */ public final class ContextMap implements Serializable { ///Thread local for holding instance of map associated with current thread private static final ThreadLocal c_context = new InheritableThreadLocal(); private final ContextMap m_parent; ///Container to hold map of elements private Map m_map = Collections.synchronizedMap(new HashMap()); ///Flag indicating whether this map should be readonly private transient boolean m_readOnly; /** * Get the Current ContextMap. * This method returns a ContextMap associated with current thread. If the * thread doesn't have a ContextMap associated with it then a new * ContextMap is created. * * @return the current ContextMap */ public final static ContextMap getCurrentContext() { return getCurrentContext(true); } /** * Get the Current ContextMap. * This method returns a ContextMap associated with current thread. * If the thread doesn't have a ContextMap associated with it and * autocreate is true then a new ContextMap is created. * * @param autocreate true if a ContextMap is to be created if it doesn't exist * @return the current ContextMap */ public final static ContextMap getCurrentContext(final boolean autocreate) { //Check security permission here??? ContextMap context = (ContextMap)c_context.get(); if (null == context && autocreate) { context = new ContextMap(); c_context.set(context); } return context; } /** * Bind a particular ContextMap to current thread. * * @param context the context map (may be null) */ public final static void bind(final ContextMap context) { //Check security permission here?? c_context.set(context); } /** * Default constructor. */ public ContextMap() { this(null); } /** * Constructor that sets parent contextMap. * * @param parent the parent ContextMap */ public ContextMap(final ContextMap parent) { m_parent = parent; } /** * Make the context read-only. * This makes it safe to allow untrusted code reference * to ContextMap. */ public void makeReadOnly() { m_readOnly = true; } /** * Determine if context is read-only. * * @return true if Context is read only, false otherwise */ public boolean isReadOnly() { return m_readOnly; } /** * Empty the context map. */ public void clear() { checkReadable(); m_map.clear(); } /** * Get an entry from the context. * * @param key the key to map * @param defaultObject a default object to return if key does not exist * @return the object in context */ public Object get(final String key, final Object defaultObject) { final Object object = get(key); if (null != object) return object; else return defaultObject; } /** * Get an entry from the context. * * @param key the key to map * @return the object in context or null if none with specified key */ public Object get(final String key) { final Object result = m_map.get(key); if (null == result && null != m_parent) { return m_parent.get(key); } return result; } /** * Set a value in context * * @param key the key * @param value the value (may be null) */ public void set(final String key, final Object value) { checkReadable(); if (value == null) { m_map.remove(key); } else { m_map.put(key, value); } } /** * Get the number of contexts in map. * * @return the number of contexts in map */ public int getSize() { return m_map.size(); } /** * Helper method that sets context to read-only after de-serialization. * * @return the corrected object version * @throws ObjectStreamException if an error occurs */ private Object readResolve() throws ObjectStreamException { makeReadOnly(); return this; } /** * Utility method to verify that Context is read-only. */ private void checkReadable() { if (isReadOnly()) { throw new IllegalStateException("ContextMap is read only and can not be modified"); } } }