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.appender.db;
018
019import java.util.Date;
020
021import org.apache.logging.log4j.Logger;
022import org.apache.logging.log4j.core.Core;
023import org.apache.logging.log4j.core.StringLayout;
024import org.apache.logging.log4j.core.config.Configuration;
025import org.apache.logging.log4j.core.config.plugins.Plugin;
026import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
027import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
028import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
029import org.apache.logging.log4j.core.config.plugins.PluginElement;
030import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
031import org.apache.logging.log4j.core.layout.PatternLayout;
032import org.apache.logging.log4j.spi.ThreadContextMap;
033import org.apache.logging.log4j.spi.ThreadContextStack;
034import org.apache.logging.log4j.status.StatusLogger;
035import org.apache.logging.log4j.util.ReadOnlyStringMap;
036
037/**
038 * A configuration element for specifying a database column name mapping.
039 *
040 * @since 2.8
041 */
042@Plugin(name = "ColumnMapping", category = Core.CATEGORY_NAME, printObject = true)
043public class ColumnMapping {
044
045    private static final Logger LOGGER = StatusLogger.getLogger();
046
047    private final String name;
048    private final StringLayout layout;
049    private final String literalValue;
050    private final Class<?> type;
051
052    private ColumnMapping(final String name, final StringLayout layout, final String literalValue, final Class<?> type) {
053        this.name = name;
054        this.layout = layout;
055        this.literalValue = literalValue;
056        this.type = type;
057    }
058
059    public String getName() {
060        return name;
061    }
062
063    public StringLayout getLayout() {
064        return layout;
065    }
066
067    public String getLiteralValue() {
068        return literalValue;
069    }
070
071    public Class<?> getType() {
072        return type;
073    }
074
075    @PluginBuilderFactory
076    public static Builder newBuilder() {
077        return new Builder();
078    }
079
080    public static class Builder implements org.apache.logging.log4j.core.util.Builder<ColumnMapping> {
081
082        @PluginBuilderAttribute
083        @Required(message = "No column name provided")
084        private String name;
085
086        @PluginElement("Layout")
087        private StringLayout layout;
088
089        @PluginBuilderAttribute
090        private String pattern;
091
092        @PluginBuilderAttribute
093        private String literal;
094
095        @PluginBuilderAttribute
096        @Required(message = "No conversion type provided")
097        private Class<?> type = String.class;
098
099        @PluginConfiguration
100        private Configuration configuration;
101
102        /**
103         * Column name.
104         */
105        public Builder setName(final String name) {
106            this.name = name;
107            return this;
108        }
109
110        /**
111         * Layout of value to write to database (before type conversion). Not applicable if {@link #setType(Class)} is
112         * a {@link ReadOnlyStringMap}, {@link ThreadContextMap}, or {@link ThreadContextStack}.
113         */
114        public Builder setLayout(final StringLayout layout) {
115            this.layout = layout;
116            return this;
117        }
118
119        /**
120         * Pattern to use as a {@link PatternLayout}. Convenient shorthand for {@link #setLayout(StringLayout)} with a
121         * PatternLayout.
122         */
123        public Builder setPattern(final String pattern) {
124            this.pattern = pattern;
125            return this;
126        }
127
128        /**
129         * Literal value to use for populating a column. This is generally useful for functions, stored procedures,
130         * etc. No escaping will be done on this value.
131         */
132        public Builder setLiteral(final String literal) {
133            this.literal = literal;
134            return this;
135        }
136
137        /**
138         * Class to convert value to before storing in database. If the type is compatible with {@link ThreadContextMap} or
139         * {@link ReadOnlyStringMap}, then the MDC will be used. If the type is compatible with {@link ThreadContextStack},
140         * then the NDC will be used. If the type is compatible with {@link Date}, then the event timestamp will be used.
141         */
142        public Builder setType(final Class<?> type) {
143            this.type = type;
144            return this;
145        }
146
147        public Builder setConfiguration(final Configuration configuration) {
148            this.configuration = configuration;
149            return this;
150        }
151
152        @Override
153        public ColumnMapping build() {
154            if (pattern != null) {
155                layout = PatternLayout.newBuilder()
156                    .withPattern(pattern)
157                    .withConfiguration(configuration)
158                    .build();
159            }
160            if (!(layout != null
161                || literal != null
162                || Date.class.isAssignableFrom(type)
163                || ReadOnlyStringMap.class.isAssignableFrom(type)
164                || ThreadContextMap.class.isAssignableFrom(type)
165                || ThreadContextStack.class.isAssignableFrom(type))) {
166                LOGGER.error("No layout or literal value specified and type ({}) is not compatible with " +
167                    "ThreadContextMap, ThreadContextStack, or java.util.Date", type);
168                return null;
169            }
170            return new ColumnMapping(name, layout, literal, type);
171        }
172    }
173}