ContextMap.java 5.48 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
/*
 * 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 -&gt;fred</li>
 * <li>hostname -&gt;helm.realityforge.org</li>
 * <li>ipaddress -&gt;1.2.3.4</li>
 * <li>interface -&gt;127.0.0.1</li>
 * <li>caller -&gt;com.biz.MyCaller.method(MyCaller.java:18)</li>
 * <li>source -&gt;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");
        }
    }
}