/*
* Copyright (c) 2009, 2012 IBM Corp.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Dave Locke - initial API and implementation and/or initial documentation
*/
package org.eclipse.paho.client.mqttv3.internal.trace;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class TraceFileDestination implements TraceDestination {
private boolean enabled = false;
private DataOutputStream dos;
private ByteArrayOutputStream buffer;
private FileOutputStream out;
private File tracePropertiesFile;
private File traceDirectory;
private int fileCount;
private int maxFileSize;
private int fileIndex = 0;
private int currentFileSize = 0;
private Properties traceProperties;
private long tracePropertiesUpdate = 0;
/**
* System property "org.eclipse.paho.client.mqttv3.trace" defines the location of the trace
* properties files. Default: mqtt-trace.properties
*
* org.eclipse.paho.client.mqttv3.trace.outputName
* directory to write the trace to. Default: current directory
* org.eclipse.paho.client.mqttv3.trace.count
* the number of trace files to write. Default: 1
* org.eclipse.paho.client.mqttv3.trace.limit
* maximum file size in bytes (only applies if count>1). Default: 5000000
*
* org.eclipse.paho.client.mqttv3.trace.client.[client id].status
* set trace status for a specific client
* values: 'on' 'off'
* org.eclipse.paho.client.mqttv3.trace.client.*.status
* set trace status for all clients. Setting to 'on' overrides individual clients set to 'off'
*/
public TraceFileDestination() {
traceProperties = new Properties();
String tracePropertiesFilename = System.getProperty("org.eclipse.paho.client.mqttv3.trace");
if (tracePropertiesFilename == null) {
tracePropertiesFile = new File(System.getProperty("user.dir"),"mqtt-trace.properties");
} else {
tracePropertiesFile = new File(tracePropertiesFilename);
}
updateTraceProperties();
}
/**
* Updates the trace properties from the trace properties file and
* initialises tracing appropriately.
* @return Whether trace is now enabled after the refresh. If the refresh
* and trace setup was successful then it will be enabled, otherwise it
* will have been disabled.
*/
private boolean updateTraceProperties() {
//If the trace properties file was found
if (tracePropertiesFile.exists()) {
//If it has changed since the last read
if (tracePropertiesFile.lastModified() != tracePropertiesUpdate) {
//Try to load the trace properties from the file
try {
traceProperties.load(new FileInputStream(tracePropertiesFile));
tracePropertiesUpdate = tracePropertiesFile.lastModified();
}
//Catch any problems - set trace to NOT enabled & return
catch (Exception e) {
traceProperties.clear();
tracePropertiesUpdate = 0;
enabled = false;
return false;
}
/*
* Set the trace properties for this class.
*/
String directory = traceProperties.getProperty("org.eclipse.paho.client.mqttv3.trace.outputName", System.getProperty("user.dir"));
traceDirectory = new File(directory);
//If the trace directory does not already exist - then cannot trace
//Note: this matches MQ default behaviour
if (!traceDirectory.exists()) {
traceProperties.clear();
tracePropertiesUpdate = 0;
enabled = false;
return false;
}
fileCount = Integer.parseInt(traceProperties.getProperty("org.eclipse.paho.client.mqttv3.trace.count", "1"));
maxFileSize = Integer.parseInt(traceProperties.getProperty("org.eclipse.paho.client.mqttv3.trace.limit", "5000000"));
//Initialise the trace file and output stream
initialiseFile();
//If the output stream is still null - then error initialising trace file - cannot trace
if(out == null) {
traceProperties.clear();
tracePropertiesUpdate = 0;
enabled = false;
return false;
}
else {
buffer = new ByteArrayOutputStream();
dos = new DataOutputStream(buffer);
enabled = true;
return true;
}
}
//Else file has NOT changed - no need to refresh anything - just return whether we are enabled from last time
else {
return enabled;
}
}
//Else trace properties file NOT found - clear existing and set tracing to NOT enabled
else {
traceProperties.clear();
tracePropertiesUpdate = 0;
enabled = false;
return false;
}
}
public boolean isEnabled(String resource) {
return enabled && ("on".equalsIgnoreCase(traceProperties.getProperty("org.eclipse.paho.client.mqttv3.trace.client.*.status")) ||
"on".equalsIgnoreCase(traceProperties.getProperty("org.eclipse.paho.client.mqttv3.trace.client."+resource+".status")) );
}
public void initialiseFile() {
if (out != null) {
try {
out.close();
} catch (IOException e) {
}
out = null;
}
currentFileSize = 0;
File traceFile = new File(traceDirectory,"mqtt-"+fileIndex+".trc");
if (traceFile.exists()) {
traceFile.delete();
}
try {
out = new FileOutputStream(traceFile);
} catch (FileNotFoundException e) {
enabled = false;
out = null;
}
}
public synchronized void write(TracePoint point) {
try {
dos.writeShort(point.source);
dos.writeLong(point.timestamp);
byte meta = point.type;
if (point.inserts != null && point.inserts.length > 0) {
meta |= 0x20;
}
if (point.throwable != null) {
meta |= 0x40;
}
dos.writeByte(meta);
dos.writeShort(point.id);
dos.writeUTF(point.threadName);
if (point.inserts != null && point.inserts.length > 0) {
dos.writeShort(point.inserts.length);
for (int i=0;i<point.inserts.length;i++) {
if (point.inserts[i]!=null) {
dos.writeUTF(point.inserts[i].toString());
} else {
dos.writeUTF("null");
}
}
}
if (point.throwable != null) {
StackTraceElement[] stack = point.throwable.getStackTrace();
dos.writeShort(stack.length+1);
dos.writeUTF(point.throwable.toString());
for (int i=0;i<stack.length;i++) {
dos.writeUTF(stack[i].toString());
}
}
if (fileCount > 1 && currentFileSize + buffer.size() > maxFileSize) {
// bump the file
fileIndex++;
if (fileIndex == fileCount) {
fileIndex = 0;
}
initialiseFile();
}
if (out != null) {
currentFileSize += buffer.size();
buffer.writeTo(out);
out.flush();
}
buffer.reset();
} catch(Exception e) {
enabled = false;
}
}
}