org.coos.plugin.logback.impl.COOSAppenderBase Maven / Gradle / Ivy
/**
* COOS - Connected Objects Operating System (www.connectedobjects.org).
*
* Copyright (C) 2009 Telenor ASA and Tellu AS. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* You may also contact one of the following for additional information:
* Telenor ASA, Snaroyveien 30, N-1331 Fornebu, Norway (www.telenor.no)
* Tellu AS, Hagalokkveien 13, N-1383 Asker, Norway (www.tellu.no)
*/
package org.coos.plugin.logback.impl;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.coos.plugin.logagent.impl.LogAgentEndpoint;
import ch.qos.logback.core.AppenderBase;
import ch.qos.logback.core.boolex.EvaluationException;
import ch.qos.logback.core.boolex.EventEvaluator;
import ch.qos.logback.core.spi.PreSerializationTransformer;
/**
* Base class for CoosAppender implementations.
*
* @author Robert Bjærum, Tellu AS
*
*/
public abstract class COOSAppenderBase extends AppenderBase {
/**
* The default end-point URI to which of the remote logging server (coos://logServer)
*/
static final String DEFAULT_LOGSERVER_URI = "coos://logServer";
/**
* The default reconnection delay (30000 milliseconds or 30 seconds).
*/
static final int DEFAULT_RECONNECTION_DELAY = 30000;
protected EventEvaluator eventEvaluator;
private String logServerUri = DEFAULT_LOGSERVER_URI;
private int reconnectionDelay = DEFAULT_RECONNECTION_DELAY;
private boolean enabled = true;
private Connector connector;
private volatile LogAgentEndpoint endpoint = null;
/* Sequence number counting logging events sent through this appender. */
private int eventSeq;
public COOSAppenderBase() {
this.logServerUri = DEFAULT_LOGSERVER_URI;
}
/**
* The EventEvaluator option takes a string value representing the name of the class
* implementing the {@link EventEvaluators} interface. A corresponding object will be
* instantiated and assigned as the event evaluator for the COOSAppender.
*/
public void setEvaluator(EventEvaluator eventEvaluator) {
this.eventEvaluator = eventEvaluator;
}
/**
* Returns the value of the LogServerUri option.
*
* @return
*/
public String getLogServerUri() {
return logServerUri;
}
/**
* The LogServerUri option takes a string representing the COOS address URI of the COOS
* Log Server to receive log messages.
*
* @param logServerUri
* the log server URI (default: coos://logServer)
* @throws IllegalArgumentException
* if null or empty
*/
public void setLogServerUri(String logServerUri) {
if (logServerUri == null || logServerUri.isEmpty()) {
throw new IllegalArgumentException("Property logServerUri cannot be null or empty");
}
this.logServerUri = logServerUri;
}
/**
* The ReconnectionDelay option takes a positive integer representing the number of
* milliseconds to wait between each failed connection attempt to the server. The default value
* of this option is 30000 which corresponds to 30 seconds.
*
*
* Setting this option to zero turns off reconnection capability.
*/
public void setReconnectionDelay(int delay) {
this.reconnectionDelay = delay;
}
/**
* Returns value of the ReconnectionDelay option.
*/
public int getReconnectionDelay() {
return reconnectionDelay;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Start the appender. The method isStarted() will return true if errors did not occur. It may
* still not be connected, since the Connector may be active.
*/
@Override
public void start() {
if (!enabled || isStarted()) {
return;
}
if (this.eventEvaluator == null) {
addError("No EventEvaluator is set for appender [" + name + "].");
return;
}
this.started = true;
fireConnector();
}
/**
* Stop this appender.
*/
@Override
public void stop() {
if (!isStarted()) {
return;
}
cleanUp();
this.started = false;
}
/**
* Drop the endpoint-connection and release the underlying connector thread if it exists.
*/
public void cleanUp() {
endpoint = null;
if (connector != null) {
addInfo("Interrupting the connector.");
connector.interrupted = true;
try {
connector.join();
} catch (InterruptedException ignore) {
}
connector = null;
}
}
/**
* Return true if the appender is connected to a COOS node through a Plugin.
*
* @return true if connected
*/
public boolean isConnected() {
return (isStarted() && endpoint != null && endpoint.getPlugin().isConnected());
}
@Override
protected void append(E event) {
if (event == null || !isConnected()) {
return;
}
try {
if (eventEvaluator.evaluate(event)) {
postProcessEvent(event);
Serializable serEvent = getPST().transform(event);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// Serialize
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(serEvent);
out.close();
eventSeq = endpoint.sendLoggingEvent(logServerUri, bos.toByteArray());
}
} catch (EvaluationException ex) {
addError("COOSAppender's EventEvaluator threw an Exception" + ex);
} catch (IOException ex) {
ex.printStackTrace();
}
}
protected abstract void postProcessEvent(E event);
protected abstract PreSerializationTransformer getPST();
/*
* Sends the events in buffer
*/
protected void sendBuffer() {
}
/*
* Used by unit tests
*/
protected void waitForConnector(long timeout) throws InterruptedException {
if (connector == null) {
return;
}
connector.join(timeout);
}
public int getEventSeq() {
return eventSeq;
}
private void fireConnector() {
cleanUp();
addInfo("Starting a new connector thread.");
connector = new Connector();
connector.setDaemon(true);
connector.setPriority(Thread.MIN_PRIORITY);
connector.start();
}
/**
* The Connector will connect to the default COOS instance when it becomes available and is
* started. It does this by attempting to open a new connection every
* reconnectionDelay
milliseconds.
*/
private class Connector extends Thread {
boolean interrupted = false;
public void run() {
while (!interrupted) {
try {
LogAgentEndpoint ep = LogAgentEndpoint.getLastInstance();
addInfo("Attempting connection to LogAgentEndpoint " + ep);
if (ep != null && ep.getPlugin().isConnected()) {
addInfo("Connection established. Exiting connector thread.");
endpoint = ep;
connector = null;
break;
}
/*
* Arrive here if connection failed. If retries are disabled, turn of started
* flag.
*/
if (reconnectionDelay <= 0) {
addWarn("Could not connect and reconnection is disabled as reconnectionDelay is zero or negative");
started = false;
break;
}
sleep(reconnectionDelay);
} catch (InterruptedException e) {
addInfo("Connector interrupted. Leaving loop.");
e.printStackTrace();
break;
} catch (Exception e) {
addInfo("Unidentified Exception caught. Exception is " + e);
}
}
}
}
}