All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.server.handlers.JDBCLogHandler Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package io.undertow.server.handlers;

import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.security.api.SecurityContext;
import io.undertow.server.ExchangeCompletionListener;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.builder.HandlerBuilder;
import io.undertow.util.Headers;

import java.net.InetSocketAddress;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class JDBCLogHandler implements HttpHandler, Runnable {

    private final HttpHandler next;
    private final String formatString;
    private final ExchangeCompletionListener exchangeCompletionListener = new JDBCLogCompletionListener();

    private final Deque pendingMessages;

    //0 = not running
    //1 = queued
    //2 = running
    @SuppressWarnings("unused")
    private volatile int state = 0;
    @SuppressWarnings("unused")
    private volatile Executor executor;

    private static final AtomicIntegerFieldUpdater stateUpdater = AtomicIntegerFieldUpdater.newUpdater(JDBCLogHandler.class, "state");

    protected boolean useLongContentLength = false;

    private final DataSource dataSource;

    private String tableName;
    private String remoteHostField;
    private String userField;
    private String timestampField;
    private String virtualHostField;
    private String methodField;
    private String queryField;
    private String statusField;
    private String bytesField;
    private String refererField;
    private String userAgentField;

    @Deprecated
    public JDBCLogHandler(final HttpHandler next, final Executor logWriteExecutor, final String formatString, DataSource dataSource) {
        this(next, formatString, dataSource);
    }

    public JDBCLogHandler(final HttpHandler next, final String formatString, DataSource dataSource) {
        this.next = next;
        this.formatString = formatString;
        this.dataSource = dataSource;

        tableName = "access";
        remoteHostField = "remoteHost";
        userField = "userName";
        timestampField = "timestamp";
        virtualHostField = "virtualHost";
        methodField = "method";
        queryField = "query";
        statusField = "status";
        bytesField = "bytes";
        refererField = "referer";
        userAgentField = "userAgent";
        this.pendingMessages = new ConcurrentLinkedDeque<>();
    }

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        exchange.addExchangeCompleteListener(exchangeCompletionListener);
        next.handleRequest(exchange);
    }

    private class JDBCLogCompletionListener implements ExchangeCompletionListener {

        @Override
        public void exchangeEvent(final HttpServerExchange exchange, final NextListener nextListener) {
            try {
                logMessage(formatString, exchange);
            } finally {
                nextListener.proceed();
            }
        }
    }

    public void logMessage(String pattern, HttpServerExchange exchange) {
        JDBCLogAttribute jdbcLogAttribute = new JDBCLogAttribute();

        if (pattern.equals("combined")) {
            jdbcLogAttribute.pattern = pattern;
        }
        jdbcLogAttribute.remoteHost = ((InetSocketAddress) exchange.getConnection().getPeerAddress()).getAddress().getHostAddress();
        SecurityContext sc = exchange.getSecurityContext();
        if (sc == null || !sc.isAuthenticated()) {
            jdbcLogAttribute.user = null;
        } else {
            jdbcLogAttribute.user = sc.getAuthenticatedAccount().getPrincipal().getName();
        }
        jdbcLogAttribute.query = exchange.getQueryString();

        jdbcLogAttribute.bytes = exchange.getResponseContentLength();
        if (jdbcLogAttribute.bytes < 0) {
            jdbcLogAttribute.bytes = 0;
        }

        jdbcLogAttribute.status = exchange.getStatusCode();

        if (jdbcLogAttribute.pattern.equals("combined")) {
            jdbcLogAttribute.virtualHost = exchange.getRequestHeaders().getFirst(Headers.HOST);
            jdbcLogAttribute.method = exchange.getRequestMethod().toString();
            jdbcLogAttribute.referer = exchange.getRequestHeaders().getFirst(Headers.REFERER);
            jdbcLogAttribute.userAgent = exchange.getRequestHeaders().getFirst(Headers.USER_AGENT);
        }

        this.pendingMessages.add(jdbcLogAttribute);
        int state = stateUpdater.get(this);
        if (state == 0) {
            if (stateUpdater.compareAndSet(this, 0, 1)) {
                this.executor = exchange.getConnection().getWorker();
                this.executor.execute(this);
            }
        }
    }

    /**
     * insert the log record to database
     */
    @Override
    public void run() {
        if (!stateUpdater.compareAndSet(this, 1, 2)) {
            return;
        }

        List messages = new ArrayList<>();
        JDBCLogAttribute msg = null;

        //only grab at most 1000 messages at a time
        for (int i = 0; i < 1000; ++i) {
            msg = pendingMessages.poll();
            if (msg == null) {
                break;
            }
            messages.add(msg);
        }
        try {
            if (!messages.isEmpty()) {
                writeMessage(messages);
            }
        } finally {
            Executor executor = this.executor;
            stateUpdater.set(this, 0);
            //check to see if there is still more messages
            //if so then run this again
            if (!pendingMessages.isEmpty()) {
                if (stateUpdater.compareAndSet(this, 0, 1)) {
                    executor.execute(this);
                }
            }
        }
    }

    private void writeMessage(List messages) {
        PreparedStatement ps = null;
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            conn.setAutoCommit(true);
            ps = prepareStatement(conn);
            for (JDBCLogAttribute jdbcLogAttribute : messages) {
                int numberOfTries = 2;
                while (numberOfTries > 0) {
                    try {
                        ps.clearParameters();
                        ps.setString(1, jdbcLogAttribute.remoteHost);
                        ps.setString(2, jdbcLogAttribute.user);
                        ps.setTimestamp(3, jdbcLogAttribute.timestamp);
                        ps.setString(4, jdbcLogAttribute.query);
                        ps.setInt(5, jdbcLogAttribute.status);
                        if (useLongContentLength) {
                            ps.setLong(6, jdbcLogAttribute.bytes);
                        } else {
                            if (jdbcLogAttribute.bytes > Integer.MAX_VALUE) {
                                jdbcLogAttribute.bytes = -1;
                            }
                            ps.setInt(6, (int) jdbcLogAttribute.bytes);
                        }
                        ps.setString(7, jdbcLogAttribute.virtualHost);
                        ps.setString(8, jdbcLogAttribute.method);
                        ps.setString(9, jdbcLogAttribute.referer);
                        ps.setString(10, jdbcLogAttribute.userAgent);

                        ps.executeUpdate();
                        numberOfTries = 0;
                    } catch (SQLException e) {
                        UndertowLogger.ROOT_LOGGER.failedToWriteJdbcAccessLog(e);
                    }
                    numberOfTries--;
                }
            }
            ps.close();
        } catch (SQLException e) {
            UndertowLogger.ROOT_LOGGER.errorWritingJDBCLog(e);
        } finally {
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    UndertowLogger.ROOT_LOGGER.debug("Exception closing prepared statement", e);
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    UndertowLogger.ROOT_LOGGER.debug("Exception closing connection", e);
                }
            }
        }
    }

    /**
     * For tests only. Blocks the current thread until all messages are written Just does a busy wait.
     * 

* DO NOT USE THIS OUTSIDE OF A TEST */ void awaitWrittenForTest() throws InterruptedException { while (!pendingMessages.isEmpty()) { Thread.sleep(10); } while (state != 0) { Thread.sleep(10); } } private PreparedStatement prepareStatement(Connection conn) throws SQLException { return conn.prepareStatement("INSERT INTO " + tableName + " (" + remoteHostField + ", " + userField + ", " + timestampField + ", " + queryField + ", " + statusField + ", " + bytesField + ", " + virtualHostField + ", " + methodField + ", " + refererField + ", " + userAgentField + ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); } private static class JDBCLogAttribute { protected String remoteHost = ""; protected String user = ""; protected String query = ""; protected long bytes = 0; protected int status = 0; protected String virtualHost = ""; protected String method = ""; protected String referer = ""; protected String userAgent = ""; protected String pattern = "common"; protected Timestamp timestamp = new Timestamp(System.currentTimeMillis()); } public boolean isUseLongContentLength() { return useLongContentLength; } public void setUseLongContentLength(boolean useLongContentLength) { this.useLongContentLength = useLongContentLength; } public String getTableName() { return tableName; } public void setTableName(String tableName) { this.tableName = tableName; } public String getRemoteHostField() { return remoteHostField; } public void setRemoteHostField(String remoteHostField) { this.remoteHostField = remoteHostField; } public String getUserField() { return userField; } public void setUserField(String userField) { this.userField = userField; } public String getTimestampField() { return timestampField; } public void setTimestampField(String timestampField) { this.timestampField = timestampField; } public String getVirtualHostField() { return virtualHostField; } public void setVirtualHostField(String virtualHostField) { this.virtualHostField = virtualHostField; } public String getMethodField() { return methodField; } public void setMethodField(String methodField) { this.methodField = methodField; } public String getQueryField() { return queryField; } public void setQueryField(String queryField) { this.queryField = queryField; } public String getStatusField() { return statusField; } public void setStatusField(String statusField) { this.statusField = statusField; } public String getBytesField() { return bytesField; } public void setBytesField(String bytesField) { this.bytesField = bytesField; } public String getRefererField() { return refererField; } public void setRefererField(String refererField) { this.refererField = refererField; } public String getUserAgentField() { return userAgentField; } public void setUserAgentField(String userAgentField) { this.userAgentField = userAgentField; } @Override public String toString() { return "JDBCLogHandler{" + "formatString='" + formatString + '\'' + '}'; } public static class Builder implements HandlerBuilder { @Override public String name() { return "jdbc-access-log"; } @Override public Map> parameters() { Map> params = new HashMap<>(); params.put("format", String.class); params.put("datasource", String.class); params.put("tableName", String.class); params.put("remoteHostField", String.class); params.put("userField", String.class); params.put("timestampField", String.class); params.put("virtualHostField", String.class); params.put("methodField", String.class); params.put("queryField", String.class); params.put("statusField", String.class); params.put("bytesField", String.class); params.put("refererField", String.class); params.put("userAgentField", String.class); return params; } @Override public Set requiredParameters() { return Collections.singleton("datasource"); } @Override public String defaultParameter() { return "datasource"; } @Override public HandlerWrapper build(Map config) { String datasourceName = (String) config.get("datasource"); try { DataSource ds = (DataSource) new InitialContext().lookup((String) config.get("datasource")); String format = (String) config.get("format"); return new Wrapper(format, ds, (String)config.get("tableName"), (String)config.get("remoteHostField"), (String)config.get("userField"), (String)config.get("timestampField"), (String)config.get("virtualHostField"), (String)config.get("methodField"), (String)config.get("queryField"), (String)config.get("statusField"), (String)config.get("bytesField"), (String)config.get("refererField"), (String)config.get("userAgentField")); } catch (NamingException ex) { throw UndertowMessages.MESSAGES.datasourceNotFound(datasourceName); } } } private static class Wrapper implements HandlerWrapper { private final DataSource datasource; private final String format; private final String tableName; private final String remoteHostField; private final String userField; private final String timestampField; private final String virtualHostField; private final String methodField; private final String queryField; private final String statusField; private final String bytesField; private final String refererField; private final String userAgentField; private Wrapper(String format, DataSource datasource, String tableName, String remoteHostField, String userField, String timestampField, String virtualHostField, String methodField, String queryField, String statusField, String bytesField, String refererField, String userAgentField) { this.datasource = datasource; this.tableName = tableName; this.remoteHostField = remoteHostField; this.userField = userField; this.timestampField = timestampField; this.virtualHostField = virtualHostField; this.methodField = methodField; this.queryField = queryField; this.statusField = statusField; this.bytesField = bytesField; this.refererField = refererField; this.userAgentField = userAgentField; this.format = "combined".equals(format) ? "combined" : "common"; } @Override public HttpHandler wrap(HttpHandler handler) { JDBCLogHandler jdbc = new JDBCLogHandler(handler, format, datasource); if(tableName != null) { jdbc.setTableName(tableName); } if(remoteHostField != null) { jdbc.setRemoteHostField(remoteHostField); } if(userField != null) { jdbc.setUserField(userField); } if(timestampField != null) { jdbc.setTimestampField(timestampField); } if(virtualHostField != null) { jdbc.setVirtualHostField(virtualHostField); } if(methodField != null) { jdbc.setMethodField(methodField); } if(queryField != null) { jdbc.setQueryField(queryField); } if(statusField != null) { jdbc.setStatusField(statusField); } if(bytesField != null) { jdbc.setBytesField(bytesField); } if(refererField != null) { jdbc.setRefererField(refererField); } if(userAgentField != null) { jdbc.setUserAgentField(userAgentField); } return jdbc; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy