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     */
017    package org.apache.logging.log4j.core.appender.db;
018    
019    import java.util.concurrent.locks.Lock;
020    import java.util.concurrent.locks.ReadWriteLock;
021    import java.util.concurrent.locks.ReentrantReadWriteLock;
022    
023    import org.apache.logging.log4j.core.Filter;
024    import org.apache.logging.log4j.core.Layout;
025    import org.apache.logging.log4j.core.LogEvent;
026    import org.apache.logging.log4j.core.appender.AbstractAppender;
027    import org.apache.logging.log4j.core.appender.AppenderRuntimeException;
028    
029    /**
030     * An abstract Appender for writing events to a database of some type, be it relational or NoSQL. All database appenders
031     * should inherit from this base appender. Three implementations are currently provided:
032     * {@link org.apache.logging.log4j.core.appender.db.jdbc JDBC}, {@link org.apache.logging.log4j.core.appender.db.jpa
033     * JPA}, and {@link org.apache.logging.log4j.core.appender.db.nosql NoSQL}.
034     * 
035     * @param <T> Specifies which type of {@link AbstractDatabaseManager} this Appender requires.
036     */
037    public abstract class AbstractDatabaseAppender<T extends AbstractDatabaseManager> extends AbstractAppender<LogEvent> {
038        private final ReadWriteLock lock = new ReentrantReadWriteLock();
039        private final Lock readLock = lock.readLock();
040        private final Lock writeLock = lock.writeLock();
041    
042        private T manager;
043    
044        /**
045         * Instantiates the base appender.
046         * 
047         * @param name The appender name.
048         * @param filter The filter, if any, to use.
049         * @param handleException Whether logging exceptions should be reported to the application.
050         * @param manager The matching {@link AbstractDatabaseManager} implementation.
051         */
052        protected AbstractDatabaseAppender(final String name, final Filter filter, final boolean handleException,
053                                           final T manager) {
054            super(name, filter, null, handleException);
055            this.manager = manager;
056        }
057    
058        /**
059         * This always returns {@code null}, as database appenders do not use a single layout. The JPA and NoSQL appenders
060         * do not use a layout at all. The JDBC appender has a layout-per-column pattern.
061         * 
062         * @return {@code null}.
063         */
064        @Override
065        public final Layout<LogEvent> getLayout() {
066            return null;
067        }
068    
069        /**
070         * Returns the underlying manager in use within this appender.
071         * 
072         * @return the manager.
073         */
074        public final T getManager() {
075            return this.manager;
076        }
077    
078        @Override
079        public final void start() {
080            if (this.getManager() == null) {
081                LOGGER.error("No AbstractDatabaseManager set for the appender named [" + this.getName() + "].");
082            }
083            super.start();
084            if (this.getManager() != null) {
085                this.getManager().connect();
086            }
087        }
088    
089        @Override
090        public final void stop() {
091            super.stop();
092            if (this.getManager() != null) {
093                this.getManager().release();
094            }
095        }
096    
097        @Override
098        public final void append(final LogEvent event) {
099            this.readLock.lock();
100            try {
101                this.getManager().write(event);
102            } catch (final AppenderRuntimeException e) {
103                this.error("Unable to write to database [" + this.getManager().getName() + "] for appender [" +
104                        this.getName() + "].", e);
105                throw e;
106            } finally {
107                this.readLock.unlock();
108            }
109        }
110    
111        /**
112         * Replaces the underlying manager in use within this appender. This can be useful for manually changing the way log
113         * events are written to the database without losing buffered or in-progress events. The existing manager is
114         * released only after the new manager has been installed. This method is thread-safe.
115         *
116         * @param manager The new manager to install.
117         */
118        protected final void replaceManager(final T manager) {
119            this.writeLock.lock();
120            try {
121                final T old = this.getManager();
122                if (!manager.isConnected()) {
123                    manager.connect();
124                }
125                this.manager = manager;
126                old.release();
127            } finally {
128                this.writeLock.unlock();
129            }
130        }
131    }