Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test refactor and enhancement #1

Merged
merged 4 commits into from
Oct 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions src/main/java/com/hellblazer/process/CachedFileTailer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* (C) Copyright 2011 Hal Hildebrand, all rights reserved.
* 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 2.1 of the License, or (at your option) any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.hellblazer.process;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.commons.io.input.ReversedLinesFileReader;

/**
* Created by saptarshi.roy on 9/29/14.
*/
public class CachedFileTailer {

private List<String> lines;

private final int maxLines;
private final File file;
private final Timer expirationTimer;
private volatile boolean bufferExpired = true;
private final ReadWriteLock lock;

public CachedFileTailer(File targetFile, TimeUnit freshnessInternalTimeUnit, long freshnessInterval, int maxLines) {

this.maxLines = maxLines;
this.file = targetFile;
this.lines = new ArrayList<>(maxLines);
this.lock = new ReentrantReadWriteLock();

this.expirationTimer = new Timer();

long periodInMS = TimeUnit.MILLISECONDS.convert(freshnessInterval, freshnessInternalTimeUnit);
expirationTimer.scheduleAtFixedRate(new CacheExpirationTimer(), periodInMS, periodInMS);
}

private class CacheExpirationTimer extends TimerTask {
@Override
public void run() {
bufferExpired = true;
}
}

public List<String> getTailLines(int numLines) throws IOException {

if (numLines < 1) {
return Collections.EMPTY_LIST;
}

if (numLines > maxLines) {
numLines = maxLines;
}

// Only 1 thread should ever reload the expired buffer
if (bufferExpired && lock.writeLock().tryLock()) {
try {
loadLines();
bufferExpired = false;
} finally {
lock.writeLock().unlock();
}
}

List<String> subBuffer = new ArrayList<>(numLines);

// We want arbitrary numbers of threads to be able to read the buffer concurrently.
try {
lock.readLock().lock();

int startPos = lines.size() - numLines;
if (startPos < 0) {
startPos = 0;
}

for (int i = startPos; i < lines.size(); i++) {
subBuffer.add(lines.get(i));
}
} finally {
lock.readLock().unlock();
}

return subBuffer;
}


private void loadLines() throws IOException {
lines.clear();
ReversedLinesFileReader reader = new ReversedLinesFileReader(file);
int linesRead = 0;
String line;
while ( ((line = reader.readLine()) != null) && (linesRead++ < maxLines)) {
lines.add(line);
}

Collections.reverse(lines);
}
}
16 changes: 16 additions & 0 deletions src/main/java/com/hellblazer/process/ManagedProcess.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,22 @@ Tailer tailStdOut(TailerListener listener, long delayMillis, boolean end,

Tailer tailStdOut(TailerListener listener);

/**
* Retrieve the tail of the Stdout stream on demand.
*
* @param numLines The number of lines to retrieve from the tail of the StdOut stream. Max = 4000
* @return numLines worth of output from the tail of the StdOut stream.
*/
String getStdOutTail(int numLines);

/**
* Retrieve the tail of the Stderr stream on demand.
*
* @param numLines The number of lines to retrieve from the tail of the StdErr stream. Max = 4000
* @return numLines worth of output from the tail of the StdErr stream.
*/
String getStdErrTail(int numLines);

/**
* causes the current thread to wait, if necessary, until the process
* represented by this <code>Process</code> object has terminated. This
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,20 @@
*/
package com.hellblazer.process.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListener;

import com.hellblazer.process.CachedFileTailer;
import com.hellblazer.process.CannotStopProcessException;
import com.hellblazer.process.ManagedProcess;

Expand Down Expand Up @@ -127,6 +120,10 @@ public static void remove(File directory) throws IOException {
protected final UUID id;
protected volatile boolean terminated = false;

protected CachedFileTailer stdOutCachedFileTailer;
protected CachedFileTailer stdErrCachedFileTailer;
public static final int MAX_TAIL_BUFFER_LINES = 4000;

public AbstractManagedProcess() {
this(UUID.randomUUID());
}
Expand Down Expand Up @@ -341,6 +338,7 @@ public void setEnvironment(Map<String, String> environment) {

@Override
public synchronized void start() throws IOException {

if (isActive()) {
return;
}
Expand Down Expand Up @@ -385,6 +383,10 @@ public synchronized void start() throws IOException {
return;
}
}

this.stdOutCachedFileTailer = new CachedFileTailer(getStdOutFile(), TimeUnit.SECONDS, 3, MAX_TAIL_BUFFER_LINES);
this.stdErrCachedFileTailer = new CachedFileTailer(getStdErrFile(), TimeUnit.SECONDS, 3, MAX_TAIL_BUFFER_LINES);

}

@Override
Expand Down Expand Up @@ -428,6 +430,41 @@ public Tailer tailStdOut(TailerListener listener, long delayMillis,
reOpen, bufSize);
}

@Override
public String getStdOutTail(int numLines) {
if (!getStdOutFile().exists()) {
throw new IllegalThreadStateException(
"Process has not been started or has already exited");
}
return readLogLinesBuffer(numLines, this.stdOutCachedFileTailer);
}

@Override
public String getStdErrTail(int numLines) {
if (!getStdErrFile().exists()) {
throw new IllegalThreadStateException(
"Process has not been started or has already exited");
}
return readLogLinesBuffer(numLines, this.stdErrCachedFileTailer);
}

private String readLogLinesBuffer(int numLines, CachedFileTailer cachedFileTailer) {
try {
List<String> logLines = cachedFileTailer.getTailLines(numLines);
StringBuilder sb = new StringBuilder();
for (String logLine : logLines) {
sb.append(logLine).append("\n");
}
return sb.toString();

} catch (IOException e) {
if (log.isLoggable(Level.WARNING)) {
log.log(Level.WARNING, "Unable to read log lines", e);
}
return null;
}
}

@Override
public String toString() {
StringBuffer buf = new StringBuffer();
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/hellblazer/process/impl/JavaProcessImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,16 @@ public InputStream getStdOut() {
return process.getStdOut();
}

@Override
public String getStdOutTail(int numLines) {
return process.getStdOutTail(numLines);
}

@Override
public String getStdErrTail(int numLines) {
return process.getStdErrTail(numLines);
}

/**
* @return the List of arguments to the Java virtual machine
*/
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/hellblazer/process/impl/UnixProcess.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ protected String getPidFileName() {
/**
* @return
*/
protected String getProcessStatus(int thePid) {
if (thePid == -1) {
protected String getProcessStatus(Integer thePid) {
if ((thePid == null) || (thePid == -1)) {
return null;
}
ProcessBuilder ps = new ProcessBuilder();
Expand Down
Loading