![JAR search and dependency download from the Maven repository](/logo.png)
com.oracle.tools.runtime.AbstractApplication Maven / Gradle / Ivy
Show all versions of oracle-tools-all Show documentation
/*
* File: AbstractApplication.java
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* The contents of this file are subject to the terms and conditions of
* the Common Development and Distribution License 1.0 (the "License").
*
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License by consulting the LICENSE.txt file
* distributed with this file, or by consulting https://oss.oracle.com/licenses/CDDL
*
* 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 LICENSE.txt.
*
* MODIFICATIONS:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*/
package com.oracle.tools.runtime;
import com.oracle.tools.runtime.console.SystemApplicationConsole;
import com.oracle.tools.runtime.java.container.Container;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* An {@link AbstractApplication} is a base implementation for an
* {@link Application} that internally uses an {@link ApplicationProcess} as a
* means of representing and controlling the said {@link Application}.
*
* Copyright (c) 2011. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
*
* @author Brian Oliver
* @author Harvey Raja
*/
public abstract class AbstractApplication implements Application
{
/**
* The default timeout for the {@link Application}.
*/
public static final long DEFAULT_TIMEOUT = 60;
/**
* The default timeout {@link TimeUnit} for the {@link Application}.
*/
public static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS;
/**
* The {@link ApplicationProcess} of the executing {@link Application}.
*/
private final P m_process;
/**
* The name of the {@link Application}.
*/
private String m_name;
/**
* The {@link ApplicationConsole} that will be used for the {@link Application} I/O.
*/
private final ApplicationConsole m_console;
/**
* Should diagnostic information be enabled for the {@link Application}.
*/
private boolean m_isDiagnosticsEnabled;
/**
* The environment variables used when establishing the {@link Application}.
*/
private Properties m_environmentVariables;
/**
* The {@link Thread} that is used to capture standard output from the underlying {@link Process}.
*/
private Thread m_outThread;
/**
* The {@link Thread} that is used to capture standard error from the underlying {@link Process}.
*/
private Thread m_errThread;
/**
* The default timeout duration.
*/
private long m_defaultTimeout;
/**
* The default timeout duration {@link TimeUnit}.
*/
private TimeUnit m_defaultTimeoutUnits;
/**
* The {@link LifecycleEventInterceptor}s that must be executed for
* {@link LifecycleEvent}s on the {@link Application}.
*/
private List> m_interceptors;
/**
* Construct an {@link AbstractApplication}.
*
* @param process the {@link ApplicationProcess} representing the {@link Application}
* @param name the name of the application
* @param console the {@link ApplicationConsole} that will be used for I/O by the {@link Application}
* @param environmentVariables the environment variables used when establishing the {@link Application}
*/
public AbstractApplication(P process,
String name,
ApplicationConsole console,
Properties environmentVariables)
{
this(process, name, console, environmentVariables, false, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT, null);
}
/**
* Construct an {@link AbstractApplication}.
*
* @param process the {@link ApplicationProcess} representing the {@link Application}
* @param name the name of the application
* @param console the {@link ApplicationConsole} that will be used for I/O by the {@link Application}
* @param environmentVariables the environment variables used when establishing the {@link Application}
* @param isDiagnosticsEnabled should diagnostic information be logged/output
* @param defaultTimeout the default timeout duration
* @param defaultTimeoutUnits the default timeout duration {@link TimeUnit}
* @param interceptors the {@link LifecycleEventInterceptor}s
*/
public AbstractApplication(P process,
String name,
ApplicationConsole console,
Properties environmentVariables,
boolean isDiagnosticsEnabled,
long defaultTimeout,
TimeUnit defaultTimeoutUnits,
Iterable> interceptors)
{
m_process = process;
m_name = name;
m_console = console == null ? new SystemApplicationConsole() : console;
m_environmentVariables = environmentVariables;
m_isDiagnosticsEnabled = Settings.isDiagnosticsEnabled(isDiagnosticsEnabled);
m_defaultTimeout = defaultTimeout;
m_defaultTimeoutUnits = defaultTimeoutUnits;
// make a copy of the interceptors
m_interceptors = new ArrayList>();
if (interceptors != null)
{
for (LifecycleEventInterceptor interceptor : interceptors)
{
m_interceptors.add(interceptor);
}
}
// start a thread to redirect standard out to the console
m_outThread = new Thread(new OutputRedirector(m_name,
"out",
m_process.getInputStream(),
m_console.getOutputWriter(),
m_process.getId(),
m_isDiagnosticsEnabled));
m_outThread.setDaemon(true);
m_outThread.setName(name + " StdOut Thread");
m_outThread.start();
// start a thread to redirect standard err to the console
m_errThread = new Thread(new OutputRedirector(m_name,
"err",
m_process.getErrorStream(),
m_console.getErrorWriter(),
m_process.getId(),
m_isDiagnosticsEnabled));
m_errThread.setDaemon(true);
m_errThread.setName(name + " StdErr Thread");
m_errThread.start();
}
/**
* {@inheritDoc}
*/
@Override
public Properties getEnvironmentVariables()
{
return m_environmentVariables;
}
/**
* {@inheritDoc}
*/
@Override
public String getName()
{
return m_name;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public int destroy()
{
// terminate the process
try
{
m_process.destroy();
}
catch (Exception e)
{
// nothing to do here as we don't care
}
// close the console
m_console.close();
// close the streams being used by the process
try
{
m_process.getInputStream().close();
}
catch (IOException e)
{
// nothing to do here as we don't care
}
try
{
m_process.getOutputStream().close();
}
catch (IOException e)
{
// nothing to do here as we don't care
}
try
{
m_process.getErrorStream().close();
}
catch (IOException e)
{
// nothing to do here as we don't care
}
// terminate the thread that is reading from the process standard out
try
{
m_outThread.interrupt();
}
catch (Exception e)
{
// nothing to do here as we don't care
}
// terminate the thread that is reading from the process standard err
try
{
m_errThread.interrupt();
}
catch (Exception e)
{
// nothing to do here as we don't care
}
// wait for it to actually terminate (because the above line may not finish for a while)
// (if we don't wait the process may be left hanging/orphaned)
int result;
try
{
result = m_process.waitFor();
}
catch (InterruptedException e)
{
// nothing to do here as we don't care
result = 0;
}
// raise the starting / realized event for the application
@SuppressWarnings("rawtypes") LifecycleEvent event = new LifecycleEvent()
{
@Override
public Enum> getType()
{
return Application.EventKind.DESTROYED;
}
@Override
public Application getObject()
{
return AbstractApplication.this;
}
};
for (LifecycleEventInterceptor interceptor : this.getLifecycleInterceptors())
{
interceptor.onEvent(event);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public long getId()
{
return m_process.getId();
}
/**
* {@inheritDoc}
*/
@Override
public long getDefaultTimeout()
{
return m_defaultTimeout;
}
/**
* {@inheritDoc}
*/
@Override
public TimeUnit getDefaultTimeoutUnits()
{
return m_defaultTimeoutUnits;
}
/**
* {@inheritDoc}
*/
@Override
public Iterable> getLifecycleInterceptors()
{
return m_interceptors;
}
/**
* Obtains the underlying {@link ApplicationProcess} that controls the
* {@link Application}.
*
* @return the {@link ApplicationProcess} for the {@link Application}
*/
protected P getApplicationProcess()
{
return m_process;
}
/**
* Obtains the default timeout duration in milliseconds.
*
* @return the default timeout in milliseconds
*/
protected long getDefaultTimeoutMS()
{
return m_defaultTimeoutUnits.toMillis(m_defaultTimeout);
}
/**
* {@inheritDoc}
*/
public int waitFor() throws InterruptedException
{
return m_process.waitFor();
}
/**
* {@inheritDoc}
*/
public int exitValue()
{
return m_process.exitValue();
}
/**
* An {@link OutputRedirector} pipes output from an {@link InputStream},
* typically of some {@link Process} to an {@link ApplicationConsole}.
*/
private static class OutputRedirector implements Runnable
{
private String m_ApplicationName;
/**
* The prefix to write in-front of lines sent to the {@link ApplicationConsole}.
*/
private String m_prefix;
/**
* The {@link ApplicationProcess} identifier.
*/
private long m_processId;
/**
* Should diagnostic information be logged/output.
*/
private boolean m_isDiagnosticsEnabled;
/**
* The {@link InputStream} from which context will be read.
*/
private InputStream m_inputStream;
/**
* The {@link PrintWriter} to which the content read from the
* {@link InputStream} will be written.
*/
private PrintWriter m_outputWriter;
/**
* Constructs an {@link OutputRedirector}.
*
* @param applicationName the name of the application
* @param prefix the prefix to output on each console line
* (typically this is the abbreviation of the stream
* like "stderr" or "stdout")
* @param inputStream the {@link InputStream} from which to read content
* @param outputWriter the {@link PrintWriter} to which to write content
* @param processId the {@link ApplicationProcess} identifier
* @param isDiagnosticsEnabled should diagnostic information be logged/output
*/
private OutputRedirector(String applicationName,
String prefix,
InputStream inputStream,
PrintWriter outputWriter,
long processId,
boolean isDiagnosticsEnabled)
{
m_ApplicationName = applicationName;
m_prefix = prefix;
m_inputStream = inputStream;
m_outputWriter = outputWriter;
m_processId = processId;
m_isDiagnosticsEnabled = isDiagnosticsEnabled;
}
/**
* {@inheritDoc}
*/
public void run()
{
long lineNumber = 1;
try
{
BufferedReader reader =
new BufferedReader(new InputStreamReader(new BufferedInputStream(m_inputStream)));
while (true)
{
String line = reader.readLine();
if (line == null)
{
break;
}
String output = String.format("[%s:%s%s] %4d: %s\n",
m_ApplicationName,
m_prefix,
m_processId < 0 ? "" : ":" + m_processId,
lineNumber++,
line);
if (m_isDiagnosticsEnabled)
{
Container.getPlatformScope().getStandardOutput().print(output);
}
m_outputWriter.print(output);
m_outputWriter.flush();
}
}
catch (Exception exception)
{
// SKIP: deliberately empty as we safely assume exceptions
// are always due to process termination.
}
try
{
String output = String.format("[%s:%s%s] %4d: (terminated)\n",
m_ApplicationName,
m_prefix,
m_processId < 0 ? "" : ":" + m_processId,
lineNumber);
if (m_isDiagnosticsEnabled)
{
Container.getPlatformScope().getStandardOutput().print(output);
}
m_outputWriter.print(output);
m_outputWriter.flush();
}
catch (Exception e)
{
// SKIP: deliberately empty as we safely assume exceptions
// are always due to process termination.
}
}
}
}