001: /*
002: * Copyright (C) The Apache Software Foundation. All rights reserved.
003: *
004: * This software is published under the terms of the Apache Software License
005: * version 1.1, a copy of which has been included with this distribution in
006: * the LICENSE file.
007: */
008: package org.jivesoftware.util.log;
009:
010: import java.io.ObjectStreamException;
011: import java.io.Serializable;
012: import java.util.Collections;
013: import java.util.HashMap;
014: import java.util.Map;
015:
016: /**
017: * The ContextMap contains non-hierarchical context information
018: * relevent to a particular LogEvent. It may include information
019: * such as;
020: * <p/>
021: * <ul>
022: * <li>user ->fred</li>
023: * <li>hostname ->helm.realityforge.org</li>
024: * <li>ipaddress ->1.2.3.4</li>
025: * <li>interface ->127.0.0.1</li>
026: * <li>caller ->com.biz.MyCaller.method(MyCaller.java:18)</li>
027: * <li>source ->1.6.3.2:33</li>
028: * </ul>
029: * The context is bound to a thread (and inherited by sub-threads) but
030: * it can also be added to by LogTargets.
031: *
032: * @author <a href="mailto:peter@apache.org">Peter Donald</a>
033: */
034: public final class ContextMap implements Serializable {
035: ///Thread local for holding instance of map associated with current thread
036: private static final ThreadLocal c_context = new InheritableThreadLocal();
037:
038: private final ContextMap m_parent;
039:
040: ///Container to hold map of elements
041: private Map m_map = Collections.synchronizedMap(new HashMap());
042:
043: ///Flag indicating whether this map should be readonly
044: private transient boolean m_readOnly;
045:
046: /**
047: * Get the Current ContextMap.
048: * This method returns a ContextMap associated with current thread. If the
049: * thread doesn't have a ContextMap associated with it then a new
050: * ContextMap is created.
051: *
052: * @return the current ContextMap
053: */
054: public final static ContextMap getCurrentContext() {
055: return getCurrentContext(true);
056: }
057:
058: /**
059: * Get the Current ContextMap.
060: * This method returns a ContextMap associated with current thread.
061: * If the thread doesn't have a ContextMap associated with it and
062: * autocreate is true then a new ContextMap is created.
063: *
064: * @param autocreate true if a ContextMap is to be created if it doesn't exist
065: * @return the current ContextMap
066: */
067: public final static ContextMap getCurrentContext(
068: final boolean autocreate) {
069: //Check security permission here???
070: ContextMap context = (ContextMap) c_context.get();
071:
072: if (null == context && autocreate) {
073: context = new ContextMap();
074: c_context.set(context);
075: }
076:
077: return context;
078: }
079:
080: /**
081: * Bind a particular ContextMap to current thread.
082: *
083: * @param context the context map (may be null)
084: */
085: public final static void bind(final ContextMap context) {
086: //Check security permission here??
087: c_context.set(context);
088: }
089:
090: /**
091: * Default constructor.
092: */
093: public ContextMap() {
094: this (null);
095: }
096:
097: /**
098: * Constructor that sets parent contextMap.
099: *
100: * @param parent the parent ContextMap
101: */
102: public ContextMap(final ContextMap parent) {
103: m_parent = parent;
104: }
105:
106: /**
107: * Make the context read-only.
108: * This makes it safe to allow untrusted code reference
109: * to ContextMap.
110: */
111: public void makeReadOnly() {
112: m_readOnly = true;
113: }
114:
115: /**
116: * Determine if context is read-only.
117: *
118: * @return true if Context is read only, false otherwise
119: */
120: public boolean isReadOnly() {
121: return m_readOnly;
122: }
123:
124: /**
125: * Empty the context map.
126: */
127: public void clear() {
128: checkReadable();
129:
130: m_map.clear();
131: }
132:
133: /**
134: * Get an entry from the context.
135: *
136: * @param key the key to map
137: * @param defaultObject a default object to return if key does not exist
138: * @return the object in context
139: */
140: public Object get(final String key, final Object defaultObject) {
141: final Object object = get(key);
142:
143: if (null != object)
144: return object;
145: else
146: return defaultObject;
147: }
148:
149: /**
150: * Get an entry from the context.
151: *
152: * @param key the key to map
153: * @return the object in context or null if none with specified key
154: */
155: public Object get(final String key) {
156: final Object result = m_map.get(key);
157:
158: if (null == result && null != m_parent) {
159: return m_parent.get(key);
160: }
161:
162: return result;
163: }
164:
165: /**
166: * Set a value in context
167: *
168: * @param key the key
169: * @param value the value (may be null)
170: */
171: public void set(final String key, final Object value) {
172: checkReadable();
173:
174: if (value == null) {
175: m_map.remove(key);
176: } else {
177: m_map.put(key, value);
178: }
179: }
180:
181: /**
182: * Get the number of contexts in map.
183: *
184: * @return the number of contexts in map
185: */
186: public int getSize() {
187: return m_map.size();
188: }
189:
190: /**
191: * Helper method that sets context to read-only after de-serialization.
192: *
193: * @return the corrected object version
194: * @throws ObjectStreamException if an error occurs
195: */
196: private Object readResolve() throws ObjectStreamException {
197: makeReadOnly();
198: return this ;
199: }
200:
201: /**
202: * Utility method to verify that Context is read-only.
203: */
204: private void checkReadable() {
205: if (isReadOnly()) {
206: throw new IllegalStateException(
207: "ContextMap is read only and can not be modified");
208: }
209: }
210: }
|