org.netbeans.modules.glassfish.common.LogViewMgr Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.glassfish.common;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Action;
import org.netbeans.modules.glassfish.tooling.server.FetchLog;
import org.netbeans.modules.glassfish.tooling.server.FetchLogEvent;
import org.netbeans.modules.glassfish.tooling.server.FetchLogEventListener;
import org.netbeans.modules.glassfish.tooling.server.FetchLogPiped;
import org.netbeans.api.server.ServerInstance;
import org.netbeans.modules.glassfish.common.actions.*;
import org.netbeans.modules.glassfish.spi.GlassfishModule;
import org.netbeans.modules.glassfish.spi.Recognizer;
import org.netbeans.modules.glassfish.spi.RecognizerCookie;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.Utilities;
import org.openide.windows.*;
/**
* This class is capable of tailing the specified file or input stream. It
* checks for changes at the specified intervals and outputs the changes to
* the given I/O panel in NetBeans.
*
* It is now particularly tuned, in the case of files, for GlassFish V3 log
* files.
*
* FIXME Refactor: LogViewMgr should be a special case of SimpleIO
*
* @author Peter Williams
* @author Michal Mocnak
*/
public class LogViewMgr {
/** Local logger. */
private static final Logger LOGGER = GlassFishLogger.get(LogViewMgr.class);
private static final boolean strictFilter = Boolean.getBoolean("glassfish.logger.strictfilter"); // NOI18N
/**
* Amount of time in milliseconds to wait between checks of the input
* stream
*/
private static final int DELAY = Utilities.isWindows() ? 1 : 100;
/**
* Singleton model pattern
*/
private static final Map> instances =
new HashMap>();
/**
* Server URI for this log view
*/
private final String uri;
/**
* The I/O window where to output the changes
*/
private InputOutput io;
/**
* Active readers for this log view. This list contains references either
* to nothing (which means log is not active), a single file reader to
* monitor server log if the server is running outside the IDE, or two
* stream readers for servers started within the IDE.
*
* !PW not sure this complexity is worth it. Reading server log correctly
* is a major pain compared to reading server I/O streams directly. But we
* don't have that luxury for servers created outside the IDE, so this is a
* feeble attempt to have our cake and eat it too :) I'll probably regret
* it later.
*/
private final List> readers =
Collections.synchronizedList(new ArrayList>());
/**
* Creates and starts a new instance of LogViewMgr
*
* @param uri the uri of the server
*/
private LogViewMgr(final String uri) {
this.uri = uri;
io = getServerIO(uri);
if (io == null) {
return; // finish, it looks like this server instance has been unregistered
}
// clear the old output
try {
io.getOut().reset();
} catch (IOException ex) {
// no op
}
}
/**
* Returns uri specific instance of LogViewMgr
*
* @param uri the uri of the server
* @return uri specific instamce of LogViewMgr
*/
public static LogViewMgr getInstance(String uri) {
LogViewMgr logViewMgr;
synchronized (instances) {
WeakReference viewRef = instances.get(uri);
logViewMgr = viewRef != null ? viewRef.get() : null;
if(logViewMgr == null) {
logViewMgr = new LogViewMgr(uri);
instances.put(uri, new WeakReference(logViewMgr));
}
}
return logViewMgr;
}
public void ensureActiveReader(List recognizers,
FetchLog serverLog, GlassfishInstance instance) {
synchronized (readers) {
boolean activeReader = false;
for(WeakReference ref: readers) {
LoggerRunnable logger = ref.get();
if(logger != null) {
activeReader = true;
break;
}
}
if(!activeReader && serverLog != null) {
readInputStreams(recognizers,
serverLog.getInputStream() instanceof FileInputStream,
instance, serverLog);
}
}
}
/**
* Reads a newly included InputSreams.
*
* @param inputStreams InputStreams to read
*/
public void readInputStreams(List recognizers, boolean fromFile,
GlassfishInstance instance, FetchLog... serverLogs) {
synchronized (readers) {
stopReaders();
for(FetchLog serverLog : serverLogs){
// LoggerRunnable will close the stream if necessary.
LoggerRunnable logger = new LoggerRunnable(recognizers,
serverLog, fromFile, instance);
readers.add(new WeakReference(logger));
Thread t = new Thread(logger);
t.start();
}
}
}
public void stopReaders() {
synchronized (readers) {
for(WeakReference ref: readers) {
LoggerRunnable logger = ref.get();
if(logger != null) {
logger.stop();
}
}
readers.clear();
}
}
private void removeReader(LoggerRunnable logger) {
synchronized (readers) {
int size = readers.size();
for(int i = 0; i < size; i++) {
WeakReference ref = readers.get(i);
if(logger == ref.get()) {
readers.remove(i);
break;
}
}
}
}
/**
* Writes a message into output
*
* @param s message to write
*/
public synchronized void write(String s, boolean error) {
OutputWriter writer = getWriter(error);
if(writer != null) {
writer.print(s);
}
}
/**
* Writes a message into output, including a link to a portion of the
* content being written.
*
* @param s message to write
*/
public synchronized void write(String s, OutputListener link, boolean important, boolean error) {
try {
OutputWriter writer = getWriter(error);
if(writer != null) {
writer.println(s, link, important);
}
} catch(IOException ex) {
LOGGER.log(Level.FINE, ex.getLocalizedMessage(), ex);
}
}
private OutputWriter getWriter(boolean error) {
if (null == io) {
return null;
}
OutputWriter writer = error ? io.getErr() : io.getOut();
if(LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "getIOWriter: closed = {0} [ {1}" + " ]" + ", output error flag = " + "{2}", new Object[]{io.isClosed(), error ? "STDERR" : "STDOUT", writer.checkError()}); // NOI18N
}
if(writer.checkError() == true) {
InputOutput newIO = getServerIO(uri);
if(newIO == null) {
if(LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "Unable to recreate I/O for {0}, still in error state", uri); // NOI18N
}
writer = null;
} else {
io = newIO;
writer = error ? io.getErr() : io.getOut();
}
}
return writer;
}
private final Locale logLocale = getLogLocale();
private static final String logBundleName = "org.netbeans.modules.glassfish.common.resources.logging";
private final String localizedWarning = getLocalized(Level.WARNING.getName());
private final String localizedSevere = getLocalized(Level.SEVERE.getName());
private final Map localizedLevels = getLevelMap();
private Locale getLogLocale() {
// XXX detect and use server language/country/variant instead of IDE's.
String language = System.getProperty("user.language"); // NOI18N
if(language != null) {
return new Locale(language, System.getProperty("user.country", ""), System.getProperty("user.variant", "")); // NOI18N
}
return Locale.getDefault();
}
private String getLocalized(String text) {
ResourceBundle bundle = ResourceBundle.getBundle(logBundleName, logLocale);
String localized = bundle.getString(text);
return localized != null ? localized : text;
}
private Map getLevelMap() {
Map levelMap = new HashMap();
for(Level l: new Level [] { Level.ALL, Level.CONFIG, Level.FINE,
Level.FINER, Level.FINEST, Level.INFO, Level.SEVERE, Level.WARNING } ) {
String name = l.getName();
levelMap.put(name, getLocalized(name));
}
return levelMap;
}
private String getLocalizedLevel(String level) {
String localizedLevel = localizedLevels.get(level);
return localizedLevel != null ? localizedLevel : level;
}
/**
* Selects output panel
*/
public synchronized void selectIO(boolean force) {
if(LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "selectIO: closed = {0}, output error flag = {1}", new Object[]{io.isClosed(), io.getOut().checkError()}); // NOI18N
}
// Only select the output window if it's closed. This makes sure it's
// created properly and displayed. However, if the user minimizes the
// output window or switches to another one, we don't switch back.
if(io.isClosed()) {
try {
io.getOut().reset();
} catch (IOException ex) {
LOGGER.log(Level.FINE, "ignorable problem", ex);
}
io.select();
// Horrible hack. closed flag is never reset, so reset it after reopening.
invokeSetClosed(io, false);
}
if (force) {
lastVisibleCheck = 0;
io.select();
}
// If the user happened to close the OutputWindow TopComponent, reopen it.
// Don't this check too often, but often enough.
if(System.currentTimeMillis() > lastVisibleCheck + VISIBILITY_CHECK_DELAY) {
Mutex.EVENT.readAccess(new Runnable() {
@Override
public void run() {
if(visibleCheck.getAndSet(true)) {
try {
TopComponent tc = null;
if(outputTCRef != null) {
tc = outputTCRef.get();
}
if(tc == null) {
tc = WindowManager.getDefault().findTopComponent(OUTPUT_WINDOW_TCID);
if(tc != null) {
outputTCRef = new WeakReference(tc);
}
}
if(tc != null && !tc.isOpened()) {
tc.open();
}
lastVisibleCheck = System.currentTimeMillis();
} finally {
visibleCheck.set(false);
}
}
}
});
}
}
private AtomicBoolean visibleCheck = new AtomicBoolean();
private final long VISIBILITY_CHECK_DELAY = 60 * 1000;
private final String OUTPUT_WINDOW_TCID = "output"; // NOI18N
private volatile long lastVisibleCheck = 0;
private WeakReference outputTCRef =
new WeakReference(null);
private volatile Method setClosedMethod;
private void invokeSetClosed(InputOutput io, boolean closed) {
if(setClosedMethod == null) {
setClosedMethod = initSetClosedMethod(io);
}
if(setClosedMethod != null) {
try {
setClosedMethod.invoke(io, closed);
} catch (Exception ex) {
if(LOGGER.isLoggable(Level.FINER)) {
LOGGER.log(Level.FINER, "invokeSetClosed", ex); // NOI18N
}
}
}
}
private Method initSetClosedMethod(InputOutput io) {
Method method = null;
try {
Class> clazz = io.getClass();
method = clazz.getDeclaredMethod("setClosed", boolean.class); // NOI18N
method.setAccessible(true);
} catch(Exception ex) {
if(LOGGER.isLoggable(Level.FINER)) {
LOGGER.log(Level.FINER, "initSetClosedMethod", ex); // NOI18N
}
}
return method;
}
private class LoggerRunnable implements Runnable {
private final List recognizers;
private FetchLog serverLog;
private final boolean ignoreEof;
private volatile boolean shutdown;
private GlassfishInstance instance;
//private final Map properties;
public LoggerRunnable(List recognizers, FetchLog serverLog,
boolean ignoreEof, GlassfishInstance instance) {
this.recognizers = recognizers;
this.serverLog = serverLog;
this.ignoreEof = ignoreEof;
this.shutdown = false;
this.instance = instance;
}
public void stop() {
shutdown = true;
}
/**
* Implementation of the Runnable interface. Here all tailing is
* performed
*/
@SuppressWarnings("SleepWhileInLoop")
@Override
public void run() {
final String originalName = Thread.currentThread().getName();
BufferedReader reader = null;
try {
Thread.currentThread().setName(this.getClass().getName()
+ " - " + serverLog.getInputStream()); // NOI18N
reader = new BufferedReader(new InputStreamReader(
serverLog.getInputStream()));
// ignoreEof is true for log files and false for process streams.
// FIXME Should differentiate filter types more cleanly.
Filter filter = ignoreEof ? new LogFileFilter(localizedLevels) :
(uri.contains("]deployer:gfv3ee6") ? new LogFileFilter(localizedLevels) :new StreamFilter());
// read from the input stream and put all the changes to the I/O window
char [] chars = new char[1024];
int len = 0;
while(!shutdown && len != -1) {
if(ignoreEof) {
reader = followLogRotation(reader);
// For file streams, only read if there is something there.
while(!shutdown && reader.ready()) {
String text = filter.process((char) reader.read());
if(text != null) {
processLine(text);
}
}
} else {
// For process streams, check for EOF every interval.
// We don't use readLine() here because it blocks.
while(!shutdown && (len = reader.read(chars)) != -1) {
for(int i = 0; i < len; i++) {
String text = filter.process(chars[i]);
if(text != null) {
processLine(text);
}
}
if(!reader.ready()) {
break;
}
}
}
// sleep for a while when the stream is empty
try {
if (ignoreEof) {
// read from file case... not associated with a process...
// make sure there is no star
io.getErr().close();
io.getOut().close();
}
Thread.sleep(DELAY);
} catch (InterruptedException e) {
// ignore
}
}
} catch (IOException ex) {
LOGGER.log(Level.INFO, "I/O exception reading server log", ex); // NOI18N
} finally {
serverLog.close();
if(reader != null) {
try {
reader.close();
} catch (IOException ex) {
LOGGER.log(Level.INFO, "I/O exception closing stream buffer", ex); // NOI18N
}
}
removeReader(this);
Thread.currentThread().setName(originalName);
}
io.getErr().close();
io.getOut().close();
}
private void processLine(String line) {
if(LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "processing text: ''{0}''", line); // NOI18N
}
// XXX sort of a hack to eliminate specific glassfish messages that
// ought not to be printed at their current level (INFO vs FINE+).
if(strictFilter && filter(line)) {
return;
}
// Track level, color, listener
Message message = new Message(line);
message.process(recognizers);
message.print();
selectIO(false);
if (shutdown) {
// some messages get processed after the server has 'stopped'
// prevent new stars on the output caption.
io.getErr().close();
io.getOut().close();
}
}
private synchronized BufferedReader followLogRotation(
BufferedReader reader) {
BufferedReader retVal = reader;
if (instance != null && instance.getProperties() != null) {
FetchLog newServerLog = null;
String dir = instance.getProperty(
GlassfishModule.DOMAINS_FOLDER_ATTR);
if (null == dir) {
// this log cannot rotate... it isn't based on a file
return retVal;
}
try {
newServerLog = getServerLogStream(instance);
if (serverLog.getInputStream() instanceof FileInputStream
&& newServerLog.getInputStream()
instanceof FileInputStream) {
FileInputStream fis = (FileInputStream)newServerLog
.getInputStream();
long newSize = fis.getChannel().size();
long oldSize = ((FileInputStream)serverLog
.getInputStream()).getChannel().size();
if (oldSize != newSize) {
retVal = new BufferedReader(new InputStreamReader(
newServerLog.getInputStream()));
serverLog.close();
serverLog = newServerLog;
}
}
} catch (IOException ioe) {
Logger.getLogger("glassfish").log(Level.WARNING, null, ioe);
} finally {
if (null != newServerLog && !(newServerLog == serverLog)) {
newServerLog.close();
}
}
}
return retVal;
}
}
private static Pattern colorPattern = Pattern.compile(
"\\033\\[([\\d]{1,3})(?:;([\\d]{1,3}))?(?:;([\\d]{1,3}))?(?:;([\\d]{1,3}))?(?:;([\\d]{1,3}))?m"); // NOI18N
private static final Color LOG_RED = new Color(204, 0, 0);
private static final Color LOG_GREEN = new Color(0, 192, 0);
private static final Color LOG_YELLOW = new Color(204, 204, 0);
private static final Color LOG_BLUE = Color.BLUE;
private static final Color LOG_MAGENTA = new Color(204, 0, 204);
private static final Color LOG_CYAN = new Color(0, 153, 255);
private static Color [] colorTable = {
Color.BLACK, LOG_RED, LOG_GREEN, LOG_YELLOW, LOG_BLUE, LOG_MAGENTA, LOG_CYAN,
};
private class Message {
private String message;
private int level;
private Color color;
private OutputListener listener;
public Message(String line) {
message = line;
}
void process(List recognizers) {
processLevel();
processColors();
processRecognizers(recognizers);
}
private void processLevel() {
level = 0;
int colon = message.substring(0, Math.min(message.length(), 15)).indexOf(':');
if(colon != -1) {
try {
String levelPrefix = message.substring(0, colon);
level = Level.parse(levelPrefix).intValue();
} catch(IllegalArgumentException ex) {
}
}
}
private void processColors() {
try {
Matcher matcher = colorPattern.matcher(message);
boolean result = matcher.find();
if(result) {
StringBuffer sb = new StringBuffer(message.length());
do {
int count = matcher.groupCount();
for(int i = 1; i < count && matcher.group(i) != null; i++) {
int code = Integer.parseInt(matcher.group(i));
if(code >= 30 && code <= 36 && color == null) {
color = colorTable[code - 30];
}
}
matcher.appendReplacement(sb, "");
result = matcher.find();
} while(result);
matcher.appendTail(sb);
message = sb.toString();
}
} catch(Exception ex) {
Logger.getLogger("glassfish").log(Level.INFO, ex.getLocalizedMessage(), ex); // NOI18N
}
if(color == null && level > 0) {
if(level <= Level.FINE.intValue()) {
color = LOG_GREEN;
} else if(level <= Level.INFO.intValue()) {
color = Color.GRAY;
}
}
}
private void processRecognizers(List recognizers) {
// Don't run recognizers on excessively long lines
if(message.length() > 500) {
return;
}
Iterator iterator = recognizers.iterator();
while(iterator.hasNext() && listener == null) {
Recognizer r = iterator.next();
try {
listener = r.processLine(message);
} catch (Exception ex) {
Logger.getLogger("glassfish").log(Level.INFO, "Recognizer " + r.getClass().getName() + " generated an exception.", ex);
}
}
}
void print() {
OutputWriter writer = getWriter(level >= 900);
try {
if(color != null && listener == null && IOColorLines.isSupported(io)) {
message = stripNewline(message);
IOColorLines.println(io, message, color);
} else if(writer != null) {
if(listener != null) {
message = stripNewline(message);
writer.println(message, listener, false);
} else {
writer.print(message);
}
}
} catch (IOException ex) {
LOGGER.log(Level.FINE, ex.getLocalizedMessage(), ex);
}
}
}
private boolean filter(String line) {
return line.startsWith("INFO: Started bundle ")
|| line.startsWith("INFO: Stopped bundle ")
|| line.startsWith("INFO: ### ")
|| line.startsWith("felix.")
|| line.startsWith("log4j:")
;
}
private static String stripNewline(String s) {
int len = s.length();
if(len > 0 && '\n' == s.charAt(len-1)) {
s = s.substring(0, len-1);
}
return s;
}
private boolean isWarning(String line) {
return line.startsWith(localizedWarning) || line.startsWith(localizedSevere);
}
private static interface Filter {
public String process(char c);
}
private abstract static class StateFilter implements Filter {
protected String message;
protected int state;
protected StringBuilder msg;
StateFilter() {
state = 0;
msg = new StringBuilder(128);
}
protected void reset() {
message = ""; // NOI18N
}
@Override
public abstract String process(char c);
}
private static final class StreamFilter extends StateFilter {
private static final Pattern messagePattern = Pattern.compile("([\\p{Lu}]{0,16}?):|([^\\r\\n]{0,24}?\\d\\d?:\\d\\d?:\\d\\d?)"); // NOI18N
private String line;
public StreamFilter() {
reset();
}
@Override
protected void reset() {
super.reset();
line = ""; // NOI18N
}
/**
* GlassFish server log format, when read from process stream:
*
* Aug 13, 2008 3:01:49 PM com.sun.enterprise.glassfish.bootstrap.ASMain main
* INFO: Launching GlassFish on Apache Felix OSGi platform
* Aug 13, 2008 3:01:50 PM com.sun.enterprise.glassfish.bootstrap.ASMainHelper setUpOSGiCache
* INFO: Removing Felix cache profile dir /space/tools/v3Aug7/domains/domain1/.felix/gf left from a previous run
*
* Welcome to Felix.
* =================
*
* Aug 13, 2008 3:01:51 PM HK2Main start
* INFO: contextRootDir = /space/tools/v3Aug7/modules
* ...
* Aug 13, 2008 3:02:14 PM
* SEVERE: Exception in thread "pool-6-thread-1"
* Aug 13, 2008 3:02:14 PM org.glassfish.scripting.rails.RailsDeployer load
* INFO: Loading application RailsGFV3 at /RailsGFV3
* Aug 13, 2008 3:02:14 PM
* SEVERE: /...absolute.path.../connection_specification.rb:232:in `establish_connection':
*
* !PW FIXME This parser should be checked for I18N stability.
*/
@Override
public String process(char c) {
String result = null;
if(c == '\n') {
if(msg.length() > 0) {
msg.append(c);
line = msg.toString();
msg.setLength(0);
Matcher matcher = messagePattern.matcher(line);
if(matcher.find() && matcher.start() == 0 && matcher.groupCount() > 1 && matcher.group(2) != null) {
result = null;
} else {
result = line;
}
}
} else if(c != '\r') {
msg.append(c);
}
return result;
}
}
private static final class LogFileFilter extends StateFilter {
private String time;
private String type;
private String version;
private String classinfo;
private String threadinfo;
private boolean multiline;
private final Map typeMap;
public LogFileFilter(Map typeMap) {
this.typeMap = typeMap;
reset();
}
@Override
protected void reset() {
super.reset();
time = type = version = classinfo = threadinfo = ""; // NOI18N
multiline = false;
}
private String getLocalizedType(String type) {
String localizedType = typeMap.get(type);
return localizedType != null ? localizedType : type;
}
/**
* GlassFish server log entry format (unformatted), when read from file:
*
* [#|
* 2008-07-20T16:59:11.738-0700|
* INFO|
* GlassFish10.0|
* org.jvnet.hk2.osgiadapter|
* _ThreadID=11;_ThreadName=Thread-6;org.glassfish.admin.config-api [1794];|
* Started bundle org.glassfish.admin.config-api [1794]
* |#]
*
* !PW FIXME This parser should be checked for I18N stability.
*/
@Override
public String process(char c) {
String result = null;
switch(state) {
case 0:
if(c == '[') {
state = 1;
} else {
if(c == '\n') {
if(msg.length() > 0) {
msg.append(c);
result = msg.toString();
msg.setLength(0);
}
} else if(c != '\r') {
msg.append(c);
}
}
break;
case 1:
if(c == '#') {
state = 2;
} else {
state = 0;
if(c == '\n') {
if(msg.length() > 0) {
msg.append(c);
result = msg.toString();
msg.setLength(0);
}
} else if(c != '\r') {
msg.append('[');
msg.append(c);
}
}
break;
case 2:
if(c == '|') {
state = 3;
msg.setLength(0);
} else {
if(c == '\n') {
if(msg.length() > 0) {
msg.append(c);
result = msg.toString();
msg.setLength(0);
}
} else if(c != '\r') {
state = 0;
msg.append('[');
msg.append('#');
msg.append(c);
}
}
break;
case 3:
if(c == '|') {
state = 4;
time = msg.toString();
msg.setLength(0);
} else {
msg.append(c);
}
break;
case 4:
if(c == '|') {
state = 5;
type = getLocalizedType(msg.toString());
msg.setLength(0);
} else {
msg.append(c);
}
break;
case 5:
if(c == '|') {
state = 6;
version = msg.toString();
msg.setLength(0);
} else {
msg.append(c);
}
break;
case 6:
if(c == '|') {
state = 7;
classinfo = msg.toString();
msg.setLength(0);
} else {
msg.append(c);
}
break;
case 7:
if(c == '|') {
state = 8;
threadinfo = msg.toString();
msg.setLength(0);
} else {
msg.append(c);
}
break;
case 8:
if(c == '|') {
state = 9;
message = msg.toString();
} else if(c == '\n') {
if(msg.length() > 0) { // suppress blank lines in multiline messages
msg.append('\n');
result = !multiline ? type + ": " + msg.toString() : msg.toString(); // NOI18N
multiline = true;
msg.setLength(0);
}
} else if(c != '\r') {
msg.append(c);
}
break;
case 9:
if(c == '#') {
state = 10;
} else {
state = 8;
msg.append('|');
msg.append(c);
}
break;
case 10:
if(c == ']') {
state = 0;
msg.setLength(0);
result = (multiline ? message : type + ": " + message) + '\n'; // NOI18N
reset();
} else {
state = 8;
msg.append('|');
msg.append('#');
msg.append(c);
}
break;
}
return result;
}
}
private static final WeakHashMap ioWeakMap =
new WeakHashMap();
public static InputOutput getServerIO(String uri) {
ServerInstance si = GlassfishInstanceProvider.getInstanceByUri(uri);
if(null == si) {
return null;
}
synchronized (ioWeakMap) {
// look in the cache
InputOutput serverIO = ioWeakMap.get(si);
if(serverIO != null) {
boolean valid = true;
if(serverIO.isClosed()) {
if(LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Output window for {0} is closed.", uri); // NOI18N
}
}
if(serverIO.getOut().checkError()) {
if(LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Standard out for {0} is in error state.", uri); // NOI18N
}
valid = false;
}
if(serverIO.getErr().checkError()) {
if(LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Standard error for {0} is in error state.", uri); // NOI18N
}
valid = false;
}
if(valid) {
return serverIO;
} else {
if(!serverIO.isClosed()) {
serverIO.closeInputOutput();
}
ioWeakMap.put(si, null);
}
}
}
// look up the node that belongs to the given server instance
Node node = si.getFullNode();
// it looks like that the server instance has been removed
if (node == null) {
return null;
}
// No server control interface...
GlassfishModule commonSupport = node.getLookup().lookup(GlassfishModule.class);
if(commonSupport == null) {
return null;
}
Action[] actions = new Action[] {
new StartServerAction.OutputAction(commonSupport),
new DebugAction.OutputAction(commonSupport),
new RestartAction.OutputAction(commonSupport),
new StopServerAction.OutputAction(commonSupport),
new RefreshAction.OutputAction(commonSupport)
};
InputOutput newIO;
synchronized (ioWeakMap) {
newIO = ioWeakMap.get(si);
if(newIO == null) {
newIO = IOProvider.getDefault().getIO(si.getDisplayName(), actions);
ioWeakMap.put(si, newIO);
}
}
return newIO;
}
public static void displayOutput(GlassfishInstance instance, Lookup lookup) {
String uri = instance.getProperty(GlassfishModule.URL_ATTR);
if (null != uri && (uri.contains("gfv3ee6wc") || uri.contains("localhost"))) {
FetchLog log = getServerLogStream(instance);
LogViewMgr mgr = LogViewMgr.getInstance(uri);
List recognizers = new ArrayList();
if (null != lookup) {
recognizers = getRecognizers(lookup.lookupAll(RecognizerCookie.class));
}
mgr.ensureActiveReader(recognizers, log, instance);
mgr.selectIO(true);
}
}
private static List getRecognizers(Collection extends RecognizerCookie> cookies) {
List recognizers;
if(!cookies.isEmpty()) {
recognizers = new LinkedList();
for(RecognizerCookie cookie: cookies) {
recognizers.addAll(cookie.getRecognizers());
}
recognizers = Collections.unmodifiableList(recognizers);
} else {
recognizers = Collections.emptyList();
}
return recognizers;
}
/**
* Log fetcher state change listener.
*/
private static class LogStateListener implements FetchLogEventListener {
/** GlassFish server instance associated with log fetcher
* and it's state change listener. */
private final GlassfishInstance instance;
/** Log fetcher associated with log fetcher state change listener. */
private final FetchLogPiped log;
/**
* Creates an instance of log fetcher state change listener.
* @param instance GlassFish server instance associated with log fetcher
* and it's state change listener.
* @param log Log fetcher associated with log fetcher state change
* listener.
*/
LogStateListener(GlassfishInstance instance, FetchLogPiped log) {
this.instance = instance;
this.log = log;
}
/**
* Remove log fetcher from instance to log fetcher mapping
* when log fetcher task is finished or has failed.
*
* @param event GlassFish log fetcher state change event.
*/
@Override
public void stateChanged(final FetchLogEvent event) {
switch (event.getState()) {
case COMPLETED: case FAILED:
FetchLog oldLog = null;
synchronized (serverInputStreams) {
FetchLog storedLog = serverInputStreams.get(instance);
if (this.log.equals(storedLog)) {
oldLog = serverInputStreams.remove(instance);
}
}
if (oldLog != null) {
oldLog.close();
}
}
}
}
/** Internal GlassFish server instance to log fetcher mapping.*/
private static final Map serverInputStreams
= new HashMap();
/**
* Add log fetcher into local instance to fetcher mapping.
*
* Log fetcher life cycle is linked with internal fetcher mapping. Fetcher
* removed from map must be cleaned up.
* Do not access internal GlassFish server instance to log fetcher mapping
* without using those access methods!
*
* @param instance GlassFish server instance used as key in local
* instance to fetcher mapping.
* @param log New GlassFish log fetcher for given server instance
* to be stored into mapping.
*/
private static void addLog(final GlassfishInstance instance,
final FetchLogPiped log) {
FetchLog oldLog;
synchronized (serverInputStreams) {
oldLog = serverInputStreams.put(instance, log);
}
log.addListener(new LogStateListener(instance, log));
if (oldLog != null) {
oldLog.close();
}
}
/**
* Remove log fetcher from local instance to fetcher mapping.
*
* Log fetcher life cycle is linked with internal fetcher mapping. Fetcher
* removed from map must be cleaned up.
* Do not access internal GlassFish server instance to log fetcher mapping
* without using those access methods!
*
* @param instance GlassFish server instance used as key in local
* instance to fetcher mapping.
* @param log New GlassFish log fetcher for given server instance
* to be stored into mapping.
*/
public static void removeLog(final GlassfishInstance instance) {
FetchLog oldLog;
synchronized (serverInputStreams) {
oldLog = serverInputStreams.remove(instance);
}
if (oldLog != null) {
oldLog.close();
}
}
/**
* Get GlassFish stored log fetcher for given server instance.
*
* GlassFish log fetchers are reused so only one log fetcher exists for
* each running server instance.
*
* @param instance GlassFish server instance used as key to retrieve
* log fetcher.
* @return GlassFish log fetcher stored for given server instance or newly
* cerated one when no log fetcher was found.
* @throws IOException
*/
private static FetchLog getServerLogStream(
final GlassfishInstance instance) {
FetchLog log;
FetchLog deadLog = null;
synchronized (serverInputStreams) {
log = serverInputStreams.get(instance);
if (log != null) {
if (log instanceof FetchLogPiped) {
// Log reading task in running state
if (((FetchLogPiped) log).isRunning()) {
return log;
// Log reading task is dead
} else {
// Postpone cleanup after synchronized block.
deadLog = log;
removeLog(instance);
}
} else {
return log;
}
}
log = FetchLogPiped.create(
GlassFishExecutors.fetchLogExecutor(), instance);
addLog(instance, (FetchLogPiped)log);
}
if (deadLog != null) {
deadLog.close();
}
return log;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy