/*
* Copyright 2016 JBoss Inc
*
* 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.apiman.gateway.engine.jdbc;
import io.apiman.gateway.engine.IComponentRegistry;
import io.apiman.gateway.engine.IMetrics;
import io.apiman.gateway.engine.metrics.RequestMetric;
import java.util.Calendar;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import org.apache.commons.dbutils.QueryRunner;
/**
* A JDBC implementation of the gateway registry. Only suitable for a
* synchronous environment - should not be used when running an async
* Gateway (e.g. vert.x).
*
* Must be configured with the JNDI location of the datasource to use.
* Example:
*
* apiman-gateway.registry=io.apiman.gateway.engine.jdbc.JdbcRegistry
* apiman-gateway.registry.datasource.jndi-location=java:jboss/datasources/apiman-gateway
*
* @author ewittman
*/
public class JdbcMetrics extends AbstractJdbcComponent implements IMetrics {
private static final int DEFAULT_QUEUE_SIZE = 10000;
protected IComponentRegistry componentRegistry;
protected final BlockingQueue<RequestMetric> queue;
private boolean stopped;
private Thread thread;
/**
* Constructor.
* @param config map of configuration options
*/
public JdbcMetrics(Map<String, String> config) {
super(config);
int queueSize = DEFAULT_QUEUE_SIZE;
String queueSizeConfig = config.get("queue.size"); //$NON-NLS-1$
if (queueSizeConfig != null) {
queueSize = new Integer(queueSizeConfig);
}
queue = new LinkedBlockingDeque<>(queueSize);
startConsumerThread();
}
/**
* Starts a thread which will serially pull information off the blocking
* queue and submit that information to hawkular metrics.
*/
private void startConsumerThread() {
stopped = false;
thread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopped) {
processQueue();
}
}
}, "JdbcMetricsConsumer"); //$NON-NLS-1$
thread.setDaemon(true);
thread.start();
}
/**
* Process the next item in the queue.
*/
@SuppressWarnings("nls")
protected void processQueue() {
try {
RequestMetric metric = queue.take();
QueryRunner run = new QueryRunner(ds);
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.setTime(metric.getRequestStart());
long rstart = cal.getTimeInMillis();
long rend = metric.getRequestEnd().getTime();
long duration = metric.getRequestDuration();
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.SECOND, 0);
long minute = cal.getTimeInMillis();
cal.set(Calendar.MINUTE, 0);
long hour = cal.getTimeInMillis();
cal.set(Calendar.HOUR_OF_DAY, 0);
long day = cal.getTimeInMillis();
cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
long week = cal.getTimeInMillis();
cal.set(Calendar.DAY_OF_MONTH, 1);
long month = cal.getTimeInMillis();
String api_org_id = metric.getApiOrgId();
String api_id = metric.getApiId();
String api_version = metric.getApiVersion();
String client_org_id = metric.getClientOrgId();
String client_id = metric.getClientId();
String client_version = metric.getClientVersion();
String plan = metric.getPlanId();
String user_id = metric.getUser();
String rtype = null;
if (metric.isFailure()) {
rtype = "failure";
} else if (metric.isError()) {
rtype = "error";
}
long bytes_up = metric.getBytesUploaded();
long bytes_down = metric.getBytesDownloaded();
// Now insert a row for the metric.
run.update("INSERT INTO gw_requests ("
+ "rstart, rend, duration, month, week, day, hour, minute, "
+ "api_org_id, api_id, api_version, "
+ "client_org_id, client_id, client_version, plan, "
+ "user_id, resp_type, bytes_up, bytes_down) VALUES ("
+ "?, ?, ?, ?, ?, ?, ?, ?,"
+ "?, ?, ?,"
+ "?, ?, ?, ?,"
+ "?, ?, ?, ?)",
rstart, rend, duration, month, week, day, hour, minute,
api_org_id, api_id, api_version,
client_org_id, client_id, client_version, plan,
user_id, rtype, bytes_up, bytes_down
);
} catch (InterruptedException ie) {
// This means that the thread was stopped.
} catch (Exception e) {
// TODO better logging of this unlikely error
System.err.println("Error adding metric to database:"); //$NON-NLS-1$
e.printStackTrace();
return;
}
}
/**
* @see io.apiman.gateway.engine.IMetrics#record(io.apiman.gateway.engine.metrics.RequestMetric)
*/
@Override
public void record(RequestMetric metric) {
try {
queue.put(metric);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @see io.apiman.gateway.engine.IMetrics#setComponentRegistry(io.apiman.gateway.engine.IComponentRegistry)
*/
@Override
public void setComponentRegistry(IComponentRegistry registry) {
this.componentRegistry = registry;
}
/**
* Called to stop the consumer thread (used for testing only).
*/
protected void stop() {
stopped = true;
thread.interrupt();
}
}