001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.lookup;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.logging.log4j.Logger;
023import org.apache.logging.log4j.core.LogEvent;
024import org.apache.logging.log4j.core.config.plugins.PluginManager;
025import org.apache.logging.log4j.core.config.plugins.PluginType;
026import org.apache.logging.log4j.status.StatusLogger;
027
028/**
029 * The Interpolator is a StrLookup that acts as a proxy for all the other StrLookups.
030 */
031public class Interpolator implements StrLookup {
032
033    private static final Logger LOGGER = StatusLogger.getLogger();
034
035    /** Constant for the prefix separator. */
036    private static final char PREFIX_SEPARATOR = ':';
037
038    private final Map<String, StrLookup> lookups = new HashMap<String, StrLookup>();
039
040    private final StrLookup defaultLookup;
041
042    public Interpolator(final StrLookup defaultLookup) {
043        this.defaultLookup = defaultLookup == null ? new MapLookup(new HashMap<String, String>()) : defaultLookup;
044        final PluginManager manager = new PluginManager("Lookup");
045        manager.collectPlugins();
046        final Map<String, PluginType<?>> plugins = manager.getPlugins();
047
048        for (final Map.Entry<String, PluginType<?>> entry : plugins.entrySet()) {
049            @SuppressWarnings("unchecked")
050            final Class<? extends StrLookup> clazz = (Class<? extends StrLookup>) entry.getValue().getPluginClass();
051            try {
052                lookups.put(entry.getKey(), clazz.newInstance());
053            } catch (final Exception ex) {
054                LOGGER.error("Unable to create Lookup for " + entry.getKey(), ex);
055            }
056        }
057    }
058
059    /**
060     * Create the default Interpolator using only Lookups that work without an event.
061     */
062    public Interpolator() {
063        this((Map<String, String>) null);
064    }
065
066    /**
067     * Create the dInterpolator using only Lookups that work without an event and initial properties.
068     */
069    public Interpolator(Map<String, String> properties) {
070        this.defaultLookup = new MapLookup(properties == null ? new HashMap<String, String>() : properties);
071        lookups.put("sys", new SystemPropertiesLookup());
072        lookups.put("env", new EnvironmentLookup());
073        lookups.put("jndi", new JndiLookup());
074        try {
075            if (Class.forName("javax.servlet.ServletContext") != null) {
076                lookups.put("web", new WebLookup());
077            }
078        } catch (ClassNotFoundException ex) {
079            LOGGER.debug("ServletContext not present - WebLookup not added");
080        } catch (Exception ex) {
081            LOGGER.error("Unable to locate ServletContext", ex);
082        }
083    }
084
085     /**
086     * Resolves the specified variable. This implementation will try to extract
087     * a variable prefix from the given variable name (the first colon (':') is
088     * used as prefix separator). It then passes the name of the variable with
089     * the prefix stripped to the lookup object registered for this prefix. If
090     * no prefix can be found or if the associated lookup object cannot resolve
091     * this variable, the default lookup object will be used.
092     *
093     * @param var the name of the variable whose value is to be looked up
094     * @return the value of this variable or <b>null</b> if it cannot be
095     * resolved
096     */
097    @Override
098    public String lookup(final String var) {
099        return lookup(null, var);
100    }
101
102    /**
103     * Resolves the specified variable. This implementation will try to extract
104     * a variable prefix from the given variable name (the first colon (':') is
105     * used as prefix separator). It then passes the name of the variable with
106     * the prefix stripped to the lookup object registered for this prefix. If
107     * no prefix can be found or if the associated lookup object cannot resolve
108     * this variable, the default lookup object will be used.
109     *
110     * @param event The current LogEvent or null.
111     * @param var the name of the variable whose value is to be looked up
112     * @return the value of this variable or <b>null</b> if it cannot be
113     * resolved
114     */
115    @Override
116    public String lookup(final LogEvent event, String var) {
117        if (var == null) {
118            return null;
119        }
120
121        final int prefixPos = var.indexOf(PREFIX_SEPARATOR);
122        if (prefixPos >= 0) {
123            final String prefix = var.substring(0, prefixPos);
124            final String name = var.substring(prefixPos + 1);
125            final StrLookup lookup = lookups.get(prefix);
126            String value = null;
127            if (lookup != null) {
128                value = event == null ? lookup.lookup(name) : lookup.lookup(event, name);
129            }
130
131            if (value != null) {
132                return value;
133            }
134            var = var.substring(prefixPos + 1);
135        }
136        if (defaultLookup != null) {
137            return event == null ? defaultLookup.lookup(var) : defaultLookup.lookup(event, var);
138        }
139        return null;
140    }
141
142    @Override
143    public String toString() {
144        final StringBuilder sb = new StringBuilder();
145        for (final String name : lookups.keySet()) {
146            if (sb.length() == 0) {
147                sb.append("{");
148            } else {
149                sb.append(", ");
150            }
151
152            sb.append(name);
153        }
154        if (sb.length() > 0) {
155            sb.append("}");
156        }
157        return sb.toString();
158    }
159}