Re: [Bug# 228886] Multiple Glassfish threads hanging in memory
Tomas Kraus <[email protected]> Tue, 21 May 2013 16:07:21 +0200
| Newsgroups | gmane.comp.java.netbeans.reviewers |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format.
--------------010805040901090205040105
Content-Type: multipart/alternative;
boundary="------------080002080101080309030708"
--------------080002080101080309030708
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Attaching hg diff -c 280234be9cb0 > sdk-280234be9cb0.diff
On 05/21/2013 03:58 PM, Petr Jiricka wrote:
> On May 21, 2013, at 1:42 PM, Tomas Kraus wrote:
>
>> *Bug 228886* <https://netbeans.org/bugzilla/show_bug.cgi?id=228886>
>> -Multiple Glassfish threads hanging in memory
>>
>> Zombie thread is left after GlassFish Log Reader thread has been
>> interrupted in build task.
>> - Start NetBeans
>> - Start VisualVM and open NetBeans, threads table, sort by Thread
>> - Create sample project ServletStateless
>> - Start GlassFish server in debug mode
>> - Undeploy all projects
>> Repeat the following cycle
>> - Run project ServletStateless
>> creates one thread FetchLogPiped
>> - Clean and build project
>> creates one thread FetchLogPiped.
>>
>> So if you run this cyle 3 times you should have 6 FetchLogPiped threads.
>> Fix in GF plugin was made in 4 steps (253015:70350e3b09dc,
>> 253069:422df5694675, 253094:1ad1e811d955 and 253176:e0be6fab29ed),
>> combined diff is attached as
>> https://netbeans.org/bugzilla/attachment.cgi?id=134564&action=diff
>>
>> I made single thread pool (ExecutorService) to be shared for all
>> GlassFish Log Reader threads. Threads are run under top level
>> ThreadGroup to not be interrupted by build task. Threads have
>> setDaemon(true) set to avoid them being NB exit blockers.
>>
>> GlassFish Tooling Library changeset is 502:280234be9cb0.
>
> Tomas, please attach a diff for this.
>
> Thanks,
> Petr
>
>
>>
>> It allows to pass external ExecutorService into GlassFish Log Reader
>> thread starting code.
>>
>> Code went trough 2 reviews - Petr Hejl and Peter Benedikovic. It was
>> cleaned up thanks to Petr Hejl notes and now I believe it's safe
>> enough to be pushed in to 7.3.1 release branch. QA testing was done
>> by Jiri Skrivanek. More details are in the bug.
>>
>
--------------080002080101080309030708
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta content="text/html; charset=ISO-8859-1"
http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
Attaching hg diff -c 280234be9cb0 > sdk-280234be9cb0.diff<br>
<br>
On 05/21/2013 03:58 PM, Petr Jiricka wrote:
<blockquote
cite="mid:[email protected]"
type="cite">
<div>
<div>On May 21, 2013, at 1:42 PM, Tomas Kraus wrote:</div>
<br class="Apple-interchange-newline">
<blockquote type="cite">
<meta http-equiv="content-type" content="text/html;
charset=ISO-8859-1">
<div bgcolor="#FFFFFF" text="#000000"> <a
moz-do-not-send="true"
href="https://netbeans.org/bugzilla/show_bug.cgi?id=228886"><b>Bug 228886</b></a>
-<span id="summary_alias_container"> <span
id="short_desc_nonedit_display">Multiple Glassfish
threads hanging in memory<br>
<br>
Zombie thread is left after GlassFish Log Reader thread
has been interrupted in build task.<br>
</span></span>
<pre class="bz_comment_text">- Start NetBeans
- Start VisualVM and open NetBeans, threads table, sort by Thread
- Create sample project ServletStateless
- Start GlassFish server in debug mode
- Undeploy all projects
Repeat the following cycle
- Run project ServletStateless
creates one thread FetchLogPiped
- Clean and build project
creates one thread FetchLogPiped.
So if you run this cyle 3 times you should have 6 FetchLogPiped threads.</pre>
Fix in GF plugin was made in 4 steps (253015:70350e3b09dc,
253069:422df5694675, 253094:1ad1e811d955 and
253176:e0be6fab29ed), combined diff is attached as <a
moz-do-not-send="true" class="moz-txt-link-freetext"
href="https://netbeans.org/bugzilla/attachment.cgi?id=134564&action=diff">https://netbeans.org/bugzilla/attachment.cgi?id=134564&action=diff</a><br>
<br>
I made single thread pool (ExecutorService) to be shared for
all <span id="summary_alias_container"><span
id="short_desc_nonedit_display">GlassFish Log Reader </span></span>threads.
Threads are run under top level ThreadGroup to not be
interrupted by build task. Threads have setDaemon(true) set
to avoid them being NB exit blockers.<br>
<br>
GlassFish Tooling Library changeset is 502:280234be9cb0.<br>
</div>
</blockquote>
<div><br>
</div>
<div>Tomas, please attach a diff for this.</div>
<div><br>
</div>
<div>Thanks,</div>
<div>Petr</div>
<div><br>
</div>
<br>
<blockquote type="cite">
<div bgcolor="#FFFFFF" text="#000000"> <br>
It allows to pass external ExecutorService into <span
id="summary_alias_container"><span
id="short_desc_nonedit_display">GlassFish Log Reader </span></span>thread
starting code.<br>
<br>
Code went trough 2 reviews - Petr Hejl and Peter
Benedikovic. It was cleaned up thanks to Petr Hejl notes and
now I believe it's safe enough to be pushed in to 7.3.1
release branch. QA testing was done by Jiri Skrivanek. More
details are in the bug.<br>
<br>
</div>
</blockquote>
</div>
<br>
</blockquote>
<br>
</body>
</html>
--------------080002080101080309030708--
--------------010805040901090205040105
Content-Type: text/x-diff;
name="sdk-280234be9cb0.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="sdk-280234be9cb0.diff"
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/main/java/org/glassfish/tools/ide/admin/ServerAdmin.java
--- a/sdk/src/main/java/org/glassfish/tools/ide/admin/ServerAdmin.java Mon Apr 29 14:14:57 2013 +0200
+++ b/sdk/src/main/java/org/glassfish/tools/ide/admin/ServerAdmin.java Tue May 14 16:27:01 2013 +0200
@@ -114,9 +114,10 @@
* <p>
* This allows to execute tasks in parallel using provided executor.
* <p/>
- * @param srv Target GlassFish server.
- * @param cmd Server administration command to me executed.
- * @param ide IDE Context object.
+ * @param executor Executor service used to start task.
+ * @param srv Target GlassFish server.
+ * @param cmd Server administration command to me executed.
+ * @param ide IDE Context object.
*/
public static <E extends Result> Future<E> exec(ExecutorService executor,
GlassFishServer srv, Command cmd, IdeContext ide) {
@@ -128,9 +129,10 @@
/**
* Execute remote administration command on GlassFish server.
* <p>
- * @param srv Target GlassFish server.
- * @param cmd Server administration command to me executed.
- * @param ide IDE Context object.
+ * @param executor Executor service used to start task.
+ * @param srv Target GlassFish server.
+ * @param cmd Server administration command to me executed.
+ * @param ide IDE Context object.
* @param listeners Listeners that are called when command execution status changes.
*/
public static <E extends Result> Future<E> exec(ExecutorService executor,
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/main/java/org/glassfish/tools/ide/server/FetchLog.java
--- a/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLog.java Mon Apr 29 14:14:57 2013 +0200
+++ b/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLog.java Tue May 14 16:27:01 2013 +0200
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 1997-2012 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
@@ -55,51 +55,6 @@
public abstract class FetchLog {
////////////////////////////////////////////////////////////////////////////
- // Static methods //
- ////////////////////////////////////////////////////////////////////////////
-
- /**
- * Constructs an instance of GlassFish server log fetcher depending
- * on server being remote or local.
- * <p/>
- * Decision if server is local or remote depends on domains folder and
- * domain name attributes stored in <code>GlassFishServer</code> object.
- * <p/>
- * @param server GlassFish server for fetching server log.
- * @param skip Skip to the end of the log file.
- * @return Newly created <code>FetchLog</code> instance.
- */
- public static FetchLog create(GlassFishServer server, boolean skip) {
- boolean isLocal = server.getDomainsFolder() != null
- && server.getDomainName() != null;
- FetchLog fetchLog = isLocal
- ? new FetchLogLocal(server, skip)
- : new FetchLogRemote(server, skip);
- return fetchLog;
- }
-
- /**
- * Constructs an instance of GlassFish server log fetcher depending
- * on server being remote or local.
- * <p/>
- * Decision if server is local or remote depends on domains folder and
- * domain name attributes stored in <code>GlassFishServer</code> object.
- * Log file is passed whole as is without skipping to the end.
- * <p/>
- * @param server GlassFish server for fetching server log.
- * @param skip Skip to the end of the log file.
- * @return Newly created <code>FetchLog</code> instance.
- */
- public static FetchLog create(GlassFishServer server) {
- boolean isLocal = server.getDomainsFolder() != null
- && server.getDomainName() != null;
- FetchLog fetchLog = isLocal
- ? new FetchLogLocal(server, false)
- : new FetchLogRemote(server, false);
- return fetchLog;
- }
-
- ////////////////////////////////////////////////////////////////////////////
// Instance attributes //
////////////////////////////////////////////////////////////////////////////
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogEvent.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogEvent.java Tue May 14 16:27:01 2013 +0200
@@ -0,0 +1,86 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2008 Sun Microsystems, Inc.
+ */
+package org.glassfish.tools.ide.server;
+
+import org.glassfish.tools.ide.admin.TaskState;
+
+/**
+ * Events in GlassFish log fetcher.
+ * <p/>
+ * @author Tomas Kraus
+ */
+public class FetchLogEvent {
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Instance attributes //
+ ////////////////////////////////////////////////////////////////////////////
+
+ /** Current log fetcher task state. */
+ private TaskState state;
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Constructors //
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Constructs an instance of event in GlassFish log fetcher.
+ * <p/>
+ * @param Current log fetcher task state.
+ */
+ FetchLogEvent(TaskState state) {
+ this.state = state;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Getters and Setters //
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Get current log fetcher task state.
+ * <p/>
+ * @rerurn Current log fetcher task state.
+ */
+ public TaskState getState() {
+ return state;
+ }
+
+}
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogEventListener.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogEventListener.java Tue May 14 16:27:01 2013 +0200
@@ -0,0 +1,58 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2013 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2008 Sun Microsystems, Inc.
+ */
+package org.glassfish.tools.ide.server;
+
+/**
+ * Listens for events in GlassFish log fetcher.
+ * <p/>
+ * @author Tomas Kraus
+ */
+public interface FetchLogEventListener {
+
+ /**
+ * Notification method called when log fetcher state was changed.
+ * <p/>
+ * @param event GlassFish log fetcher state change event.
+ */
+ public void stateChanged(final FetchLogEvent event);
+
+}
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogLocal.java
--- a/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogLocal.java Mon Apr 29 14:14:57 2013 +0200
+++ b/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogLocal.java Tue May 14 16:27:01 2013 +0200
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 1997-2012 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
@@ -42,6 +42,7 @@
package org.glassfish.tools.ide.server;
import java.io.*;
+import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import org.glassfish.tools.ide.admin.TaskState;
import org.glassfish.tools.ide.data.GlassFishServer;
@@ -72,10 +73,30 @@
* and <code>getDomainName</code> should not return null.
* @param skip Skip to the end of the log file.
*/
- FetchLogLocal(GlassFishServer server, boolean skip) {
+ FetchLogLocal(final GlassFishServer server, final boolean skip) {
super(server, skip);
}
+ /**
+ * Constructs an instance of GlassFish local server log fetcher with
+ * external {@see ExecutorService}.
+ * <p/>
+ * Super class constructor will call <code>initInputStream</code> method
+ * which initializes <code>InputStream</code> as
+ * <code>FileInputStream</code> before this constructor code is being
+ * executed.
+ * <p/>
+ * @param executor Executor service used to start task.
+ * @param server GlassFish server for fetching local server log. Both
+ * <code>getDomainsFolder</code>
+ * and <code>getDomainName</code> should not return null.
+ * @param skip Skip to the end of the log file.
+ */
+ FetchLogLocal(final ExecutorService executor, final GlassFishServer server,
+ final boolean skip) {
+ super(executor, server, skip);
+ }
+
////////////////////////////////////////////////////////////////////////////
// Methods //
////////////////////////////////////////////////////////////////////////////
@@ -135,13 +156,14 @@
@SuppressWarnings("SleepWhileInLoop")
@Override
public TaskState call() {
+ notifyListeners(TaskState.RUNNING);
InputStream fIn = initInputFile();
byte[] buff = new byte[PIPE_BUFFER_SIZE];
File logFile = ServerUtils.getServerLogFile(server);
int inCount;
long lastModified;
if (fIn == null) {
- return TaskState.FAILED;
+ return notifyListeners(TaskState.FAILED);
}
while (taksExecute) {
try {
@@ -176,25 +198,26 @@
Logger.log(Level.INFO,
"Caught InterruptedException: " + ie.getMessage());
Thread.currentThread().interrupt();
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
} catch (InterruptedIOException ie) {
Logger.log(Level.INFO,
"Caught InterruptedIOException: " + ie.getMessage());
+ ie.printStackTrace();
Thread.currentThread().interrupt();
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
} catch (IOException ioe) {
if (taksExecute) {
Logger.log(Level.INFO, "Caught IOException: ", ioe);
- return TaskState.FAILED;
+ return notifyListeners(TaskState.FAILED);
} else {
Logger.log(Level.INFO,
"Caught IOException: " + ioe.getMessage());
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
}
}
}
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
}
}
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogPiped.java
--- a/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogPiped.java Mon Apr 29 14:14:57 2013 +0200
+++ b/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogPiped.java Tue May 14 16:27:01 2013 +0200
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 1997-2012 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
@@ -50,6 +50,7 @@
import org.glassfish.tools.ide.admin.TaskState;
import org.glassfish.tools.ide.data.GlassFishServer;
import org.glassfish.tools.ide.logging.Logger;
+import org.glassfish.tools.ide.utils.LinkedList;
/**
* Fetch GlassFish log from local or remote server.
@@ -72,6 +73,101 @@
/** Log refresh delay in miliseconds. */
static final int LOG_REFRESH_DELAY = 1000;
+ ////////////////////////////////////////////////////////////////////////////
+ // Static methods //
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Constructs an instance of GlassFish server log fetcher depending
+ * on server being remote or local.
+ * <p/>
+ * Decision if server is local or remote depends on domains folder and
+ * domain name attributes stored in <code>GlassFishServer</code> object.
+ * <p/>
+ * @param server GlassFish server for fetching server log.
+ * @param skip Skip to the end of the log file.
+ * @return Newly created <code>FetchLog</code> instance.
+ */
+ public static FetchLogPiped create(final GlassFishServer server,
+ final boolean skip) {
+ boolean isLocal = server.getDomainsFolder() != null
+ && server.getDomainName() != null;
+ FetchLogPiped fetchLog = isLocal
+ ? new FetchLogLocal(server, skip)
+ : new FetchLogRemote(server, skip);
+ fetchLog.start();
+ return fetchLog;
+ }
+
+ /**
+ * Constructs an instance of GlassFish server log fetcher depending
+ * on server being remote or local.
+ * <p/>
+ * Decision if server is local or remote depends on domains folder and
+ * domain name attributes stored in <code>GlassFishServer</code> object.
+ * Log file is passed whole as is without skipping to the end.
+ * <p/>
+ * @param server GlassFish server for fetching server log.
+ * @param skip Skip to the end of the log file.
+ * @return Newly created <code>FetchLog</code> instance.
+ */
+ public static FetchLogPiped create(final GlassFishServer server) {
+ boolean isLocal = server.getDomainsFolder() != null
+ && server.getDomainName() != null;
+ FetchLogPiped fetchLog = isLocal
+ ? new FetchLogLocal(server, false)
+ : new FetchLogRemote(server, false);
+ fetchLog.start();
+ return fetchLog;
+ }
+
+ /**
+ * Constructs an instance of GlassFish server log fetcher depending
+ * on server being remote or local with external {@see ExecutorService}.
+ * <p/>
+ * Decision if server is local or remote depends on domains folder and
+ * domain name attributes stored in <code>GlassFishServer</code> object.
+ * <p/>
+ * @param executor Executor service used to start task.
+ * @param server GlassFish server for fetching server log.
+ * @param skip Skip to the end of the log file.
+ * @return Newly created <code>FetchLog</code> instance.
+ */
+ public static FetchLogPiped create(final ExecutorService executor,
+ final GlassFishServer server, final boolean skip) {
+ boolean isLocal = server.getDomainsFolder() != null
+ && server.getDomainName() != null;
+ FetchLogPiped fetchLog = isLocal
+ ? new FetchLogLocal(executor, server, skip)
+ : new FetchLogRemote(executor, server, skip);
+ fetchLog.start();
+ return fetchLog;
+ }
+
+ /**
+ * Constructs an instance of GlassFish server log fetcher depending
+ * on server being remote or local with external {@see ExecutorService}.
+ * <p/>
+ * Decision if server is local or remote depends on domains folder and
+ * domain name attributes stored in <code>GlassFishServer</code> object.
+ * Log file is passed whole as is without skipping to the end.
+ * <p/>
+ * @param executor Executor service used to start task.
+ * @param server GlassFish server for fetching server log.
+ * @param skip Skip to the end of the log file.
+ * @return Newly created <code>FetchLog</code> instance.
+ */
+ public static FetchLogPiped create(final ExecutorService executor,
+ final GlassFishServer server) {
+ boolean isLocal = server.getDomainsFolder() != null
+ && server.getDomainName() != null;
+ FetchLogPiped fetchLog = isLocal
+ ? new FetchLogLocal(executor, server, false)
+ : new FetchLogRemote(executor, server, false);
+ fetchLog.start();
+ return fetchLog;
+ }
+
////////////////////////////////////////////////////////////////////////////
// Instance attributes //
@@ -81,7 +177,7 @@
final PipedOutputStream out;
/** Running task that reads log lines from remote server. */
- final Future<TaskState> task;
+ Future<TaskState> task;
/** Executor used to run read remote server log tasks. */
private ExecutorService executor;
@@ -89,6 +185,9 @@
/** Indicate whether log lines reading task should continue or exit. */
volatile boolean taksExecute;
+ /** Listeners for state change events in GlassFish log fetcher. */
+ private final LinkedList<FetchLogEventListener> eventListeners;
+
////////////////////////////////////////////////////////////////////////////
// Constructors //
////////////////////////////////////////////////////////////////////////////
@@ -106,9 +205,9 @@
* @param server GlassFish server for fetching server log.
* @param skip Skip to the end of the log file.
*/
- @SuppressWarnings("LeakingThisInConstructor")
FetchLogPiped(final GlassFishServer server, boolean skip) {
super(server, skip);
+ this.eventListeners = new LinkedList();
try {
out = new PipedOutputStream((PipedInputStream)this.in);
} catch (IOException ioe) {
@@ -117,8 +216,7 @@
FetchLogException.OUTPUT_STREAM_EXCEPTION, ioe);
}
taksExecute = true;
-
-// executor = Executors.newFixedThreadPool(1);
+
// Temporary fix to not show fetcher threads on NetBeans exit.
executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
@@ -131,7 +229,39 @@
}
});
- task = executor.submit(this);
+ }
+
+ /**
+ * Constructs an instance of GlassFish remote server log fetcher with
+ * external {@see ExecutorService}.
+ * <p/>
+ * Super class constructor will call <code>initInputStream</code> method
+ * which initializes <code>InputStream</code> as
+ * <code>PipedInputStream</code> before this constructor code is being
+ * executed. Here we can simply connect already initialized
+ * <code>PipedInputStream</code> with newly created
+ * <code>PipedInputStream</code>.
+ * <p/>
+ * @param executor Executor service used to start task.
+ * @param server GlassFish server for fetching server log.
+ * @param skip Skip to the end of the log file.
+ */
+ @SuppressWarnings("LeakingThisInConstructor")
+ FetchLogPiped(final ExecutorService executor, final GlassFishServer server,
+ boolean skip) {
+ super(server, skip);
+ this.eventListeners = new LinkedList();
+ try {
+ out = new PipedOutputStream((PipedInputStream)this.in);
+ } catch (IOException ioe) {
+ super.close();
+ throw new FetchLogException(
+ FetchLogException.OUTPUT_STREAM_EXCEPTION, ioe);
+ }
+ taksExecute = true;
+
+ // Temporary fix to not show fetcher threads on NetBeans exit.
+ this.executor = executor;
}
////////////////////////////////////////////////////////////////////////////
@@ -161,6 +291,92 @@
////////////////////////////////////////////////////////////////////////////
/**
+ * Add GlassFish log fetcher state change listener at the end
+ * of listeners list.
+ * <p/>
+ * @param listener Listener for state change events in GlassFish log fetcher
+ * to be added. Value shall not be <code>null</code>.
+ * @throws FetchLogException When <code>listener</code> parameter
+ * is <code>null</code>.
+ */
+ public final void addListener(final FetchLogEventListener listener)
+ throws FetchLogException {
+ if (listener == null) {
+ throw new FetchLogException(
+ "Listener parameter shall not ne null.");
+ }
+ synchronized(eventListeners) {
+ eventListeners.addLast(listener);
+ }
+ }
+
+ /**
+ * Remove all occurrences of log fetcher state change listener
+ * from listeners list.
+ * <p/>
+ * @param listener Listener for state change events in GlassFish log fetcher
+ * to be removed. Value shall not be <code>null</code>.
+ * @return Value of <code>true</code> when at least one listener was removed
+ * or <code>false</code> otherwise.
+ * @throws FetchLogException When <code>listener</code> parameter
+ * is <code>null</code>.
+ */
+ public final boolean removeListener(final FetchLogEventListener listener)
+ throws FetchLogException {
+ if (listener == null) {
+ throw new FetchLogException(
+ "Listener parameter shall not ne null.");
+ }
+ boolean removed = false;
+ synchronized(eventListeners) {
+ boolean isElement = !eventListeners.isEmpty();
+ eventListeners.first();
+ while (isElement) {
+ if (listener.equals(eventListeners.getCurrent())) {
+ isElement = eventListeners.isNext();
+ eventListeners.removeAndNextOrPrevious();
+ removed = true;
+ } else {
+ isElement = eventListeners.next();
+ }
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Notify all GlassFish log fetcher state change listeners about state
+ * change event.
+ * <p/>
+ * @param state Current GlassFish log fetcher state.
+ * @return Current GlassFish log fetcher state.
+ */
+ final TaskState notifyListeners(final TaskState state) {
+ if (!eventListeners.isEmpty()) {
+ synchronized (eventListeners) {
+ boolean isElement = !eventListeners.isEmpty();
+ if (isElement) {
+ FetchLogEvent event = new FetchLogEvent(state);
+ eventListeners.first();
+ while (isElement) {
+ eventListeners.getCurrent().stateChanged(event);
+ isElement = eventListeners.next();
+ }
+ }
+ }
+ }
+ return state;
+ }
+
+ /**
+ * Start task.
+ */
+ private void start() {
+ task = executor.submit(this);
+ notifyListeners(TaskState.READY);
+ }
+
+ /**
* Stop running task if it's still running.
* <p/>
* @return Task execution result.
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogRemote.java
--- a/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogRemote.java Mon Apr 29 14:14:57 2013 +0200
+++ b/sdk/src/main/java/org/glassfish/tools/ide/server/FetchLogRemote.java Tue May 14 16:27:01 2013 +0200
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 1997-2012 Oracle and/or its affiliates. All rights reserved.
+ * Copyright 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
@@ -44,6 +44,7 @@
import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.glassfish.tools.ide.admin.CommandFetchLogData;
@@ -80,11 +81,30 @@
* @param server GlassFish server for fetching server log.
* @param skip Skip to the end of the log file.
*/
- @SuppressWarnings("LeakingThisInConstructor")
- FetchLogRemote(GlassFishServer server, boolean skip) {
+ FetchLogRemote(final GlassFishServer server, final boolean skip) {
super(server, skip);
}
+ /**
+ * Constructs an instance of GlassFish remote server log fetcher with
+ * external {@see ExecutorService}.
+ * <p/>
+ * Super class constructor will call <code>initInputStream</code> method
+ * which initializes <code>InputStream</code> as
+ * <code>PipedInputStream</code> before this constructor code is being
+ * executed. Here we can simply connect already initialized
+ * <code>PipedInputStream</code> with newly created
+ * <code>PipedInputStream</code>.
+ * <p/>
+ * @param executor Executor service used to start task.
+ * @param server GlassFish server for fetching server log.
+ * @param skip Skip to the end of the log file.
+ */
+ FetchLogRemote(final ExecutorService executor, final GlassFishServer server,
+ final boolean skip) {
+ super(executor, server, skip);
+ }
+
////////////////////////////////////////////////////////////////////////////
// Runnable call() Method //
////////////////////////////////////////////////////////////////////////////
@@ -102,9 +122,9 @@
@SuppressWarnings("SleepWhileInLoop")
@Override
public TaskState call() {
- IdeContext ctx = new IdeContext();
+ notifyListeners(TaskState.RUNNING);
Future<ResultLog> future = ServerAdmin.<ResultLog>exec(server,
- new CommandFetchLogData(), ctx);
+ new CommandFetchLogData(), new IdeContext());
try {
ResultLog result = future.get();
if (!skip && result.getState() == TaskState.COMPLETED) {
@@ -119,7 +139,7 @@
future = ServerAdmin.<ResultLog>exec(server,
new CommandFetchLogData(
result.getValue().getParamsAppendNext()),
- ctx);
+ new IdeContext());
result = future.get();
if (result.getState() == TaskState.COMPLETED) {
for (String line : result.getValue().getLines()) {
@@ -139,26 +159,26 @@
Logger.log(Level.INFO,
"Caught InterruptedException: " + ie.getMessage());
Thread.currentThread().interrupt();
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
} catch (ExecutionException ee) {
Logger.log(Level.INFO, "Caught ExecutionException: ", ee);
- return TaskState.FAILED;
+ return notifyListeners(TaskState.FAILED);
} catch (InterruptedIOException ie) {
Logger.log(Level.INFO,
"Caught InterruptedIOException: " + ie.getMessage());
Thread.currentThread().interrupt();
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
} catch (IOException ioe) {
if (taksExecute) {
Logger.log(Level.INFO, "Caught IOException: ", ioe);
- return TaskState.FAILED;
+ return notifyListeners(TaskState.FAILED);
} else {
Logger.log(Level.INFO,
"Caught IOException: " + ioe.getMessage());
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
}
}
- return TaskState.COMPLETED;
+ return notifyListeners(TaskState.COMPLETED);
}
}
diff -r 74660966b1bf -r 280234be9cb0 sdk/src/test/java/org/glassfish/tools/ide/server/FetchLogTest.java
--- a/sdk/src/test/java/org/glassfish/tools/ide/server/FetchLogTest.java Mon Apr 29 14:14:57 2013 +0200
+++ b/sdk/src/test/java/org/glassfish/tools/ide/server/FetchLogTest.java Tue May 14 16:27:01 2013 +0200
@@ -71,7 +71,7 @@
|| server.getDomainName() == null) {
fail("Glassfish server cannot be used as local");
}
- FetchLog log = FetchLog.create(server, false);
+ FetchLog log = FetchLogPiped.create(server, false);
StreamLinesList list = new StreamLinesList(log.getInputStream());
try {
Thread.sleep(2 * FetchLogRemote.LOG_REFRESH_DELAY);
@@ -130,7 +130,7 @@
fail("Cannot create empty log file: "
+ logFile.getAbsolutePath());
}
- FetchLog log = FetchLog.create(server);
+ FetchLog log = FetchLogPiped.create(server);
FileWriter out = new FileWriter(logFile);
BufferedReader in = new BufferedReader(
new InputStreamReader(log.getInputStream()));
@@ -198,7 +198,7 @@
public void testRemoteFetchLog() {
GlassFishServer server = glassFishServer();
((GlassFishServerEntity)server).setDomainsFolder(null);
- FetchLog log = FetchLog.create(server, false);
+ FetchLog log = FetchLogPiped.create(server, false);
try {
while (0 == log.getInputStream().available()) {
Thread.sleep(FetchLogRemote.LOG_REFRESH_DELAY);
--------------010805040901090205040105--