![JAR search and dependency download from the Maven repository](/logo.png)
com.oracle.tools.runtime.java.ContainerBasedJavaApplicationBuilder Maven / Gradle / Ivy
Show all versions of oracle-tools-all Show documentation
/*
* File: ContainerBasedJavaApplicationBuilder.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.java;
import com.oracle.tools.runtime.Application;
import com.oracle.tools.runtime.ApplicationConsole;
import com.oracle.tools.runtime.PropertiesBuilder;
import com.oracle.tools.runtime.java.container.Container;
import com.oracle.tools.runtime.java.container.ContainerClassLoader;
import com.oracle.tools.runtime.java.util.CallableStaticMethod;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* An {@link ContainerBasedJavaApplicationBuilder} is a {@link com.oracle.tools.runtime.java.JavaApplicationBuilder}
* that realizes {@link com.oracle.tools.runtime.java.JavaApplication}s isolated with in the current Java
* Virtual Machine in the same manner as a regular Java EE application server
* or container.
*
* Scope of Application occurs through the use of a {@link ContainerBasedJavaProcess}
* and a specialized child-first class loader provided by a {@link com.oracle.tools.runtime.java.container.ContainerClassLoader}.
*
* Caution: Care should be taken using this {@link com.oracle.tools.runtime.java.JavaApplicationBuilder}
* as all classes used by the application will be re-loaded in the Perm Generation
* (on pre Java 8 editions of Java). Without a large Perm Generation, out-of-memory
* exceptions may be thrown.
*
* Copyright (c) 2013. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
*
* @author Brian Oliver
* @author Jonathan Knight
*/
public class ContainerBasedJavaApplicationBuilder, S extends JavaApplicationSchema>
extends AbstractJavaApplicationBuilder implements JavaApplicationBuilder
{
/**
* Constructs a {@link ContainerBasedJavaApplicationBuilder}.
*/
public ContainerBasedJavaApplicationBuilder()
{
super();
}
/**
* Sets if diagnostic information should be logged/output for {@link Application}s
* produced by this builder.
*
* @param isDiagnosticsEnabled should diagnostics be output
*
* @return the builder (so that we can perform method chaining)
*/
public ContainerBasedJavaApplicationBuilder setDiagnosticsEnabled(boolean isDiagnosticsEnabled)
{
m_isDiagnosticsEnabled = isDiagnosticsEnabled;
return this;
}
/**
* {@inheritDoc}
*/
@Override
public A realize(S schema,
String applicationName,
ApplicationConsole console) throws IOException
{
try
{
// establish the System Properties for the ContainerBasedJavaApplication
Properties systemProperties = schema.getSystemPropertiesBuilder().realize();
// establish the ContainerClassLoader for the application
ContainerClassLoader classLoader = ContainerClassLoader.newInstance(applicationName,
schema.getClassPath(),
systemProperties);
// determine the ApplicationController to use to control the process
ApplicationController controller;
if (schema instanceof ApplicationController)
{
controller = (ApplicationController) schema;
}
else
{
// when the Schema doesn't define a special way to control
// ContainerBasedJavaProcesses, we default to using the
// standard approach of executing the "main" method on the
// specified Application Class
controller = new StandardController(schema.getApplicationClassName(), schema.getArguments());
}
// establish the ContainerBasedJavaProcess
ContainerBasedJavaProcess process = new ContainerBasedJavaProcess(classLoader, controller);
// start the process
process.start();
// the environment variables for the ContainerBasedJavaApplication
// will be the environment variables for the Java Virtual Machine
Properties environmentVariables = PropertiesBuilder.fromCurrentEnvironmentVariables().realize();
// delegate Application creation to the Schema
final A application = schema.createJavaApplication(process,
applicationName,
console,
environmentVariables,
systemProperties);
// let interceptors know that the application has been realized
raiseApplicationLifecycleEvent(application, Application.EventKind.REALIZED);
return application;
}
catch (Exception e)
{
throw new IOException("Failed to start ContainerBasedJavaProcess", e);
}
}
/**
* Provides the ability to start and destroy a container-based
* {@link ControllableApplication} application.
*/
public static interface ApplicationController
{
/**
* Asynchronously starts a {@link ControllableApplication}.
*
* Should an exception be raised during the execution of this method, the
* application is assumed unusable and in an unknown state, including
* that of being destroyed.
*
* @param application the {@link ControllableApplication} to start
*
* @return a {@link Future} that may be used to determine the status of
* or wait for the application to start
*/
public Future> start(ControllableApplication application);
/**
* Asynchronously destroys a {@link ControllableApplication}.
*
* Should an exception be raised during the execution of this method, the
* application is assumed unusable and in an unknown state, including
* that of being destroyed.
*
* @param application the {@link ControllableApplication} to destroy
*
* @return a {@link Future} that may be used to determine the status of
* or wait for the application to be destroyed
*/
public Future> destroy(ControllableApplication application);
}
/**
* A representation of a container-based application to allow it to be controlled
* outside of the {@link ContainerClassLoader} in which they are scoped.
*/
public static interface ControllableApplication
{
/**
* Obtains the {@link ClassLoader} used to load and start the application.
*
* @return the application {@link ClassLoader}
*/
public ClassLoader getClassLoader();
/**
* Submits the specified {@link Callable} for asynchronous execution with
* in the context of the {@link #getClassLoader()} of the application,
* returning a Future representing the result.
*
* Note: When the {@link java.util.concurrent.Callable#call()} method is
* invoked, the {@link Thread#getContextClassLoader()} will be that of
* the {@link com.oracle.tools.runtime.java.ContainerBasedJavaApplicationBuilder.ControllableApplication#getClassLoader()}s
*
* @param callable the {@link Callable} to submit
* @param the return type of the {@link Callable}
* @return a Future providing a means to access the asynchronous
* result of the invocation
*/
public Future submit(Callable callable);
}
/**
* An implementation of a {@link JavaProcess} to represent and control a
* Java application running with in a Java Virtual Machine, as part of a
* container, much like a Java EE application.
*/
public static class ContainerBasedJavaProcess implements JavaProcess, ControllableApplication
{
/**
* An {@link ExecutorService} to use for requesting asynchronous
* execution of tasks in the contained application. Typically this
* is used for executing start and destroy functionality.
*/
private ExecutorService m_executorService;
/**
* The {@link ClassLoader} that will be used to contain, scope and isolate
* the executing application.
*/
private ContainerClassLoader m_classLoader;
/**
* The {@link ApplicationController} that will be used to start/destroy
* the application.
*/
private ApplicationController m_controller;
/**
* The {@link Future} that represents the result of starting the
* application. (may be null if the {@link ApplicationController}
* didn't produce a {@link Future}).
*/
private Future> m_startFuture;
/**
* The {@link Future} that represents the result of starting the
* application. (may be null if the {@link ApplicationController}
* didn't produce a {@link Future}).
*/
private Future> m_destroyFuture;
/**
* Constructs an {@link ContainerBasedJavaProcess}.
*
* @param classLoader the {@link ClassLoader} in which to run the application
* @param controller the {@link ApplicationController}
*/
public ContainerBasedJavaProcess(ContainerClassLoader classLoader,
ApplicationController controller)
{
if (controller == null)
{
throw new NullPointerException("ApplicationController must not be null");
}
// establish an ExecutorService that we can use to asynchronously submit
// requests against the ContainerBasedJavaProcess
m_executorService = Executors.newCachedThreadPool();
m_classLoader = classLoader;
m_controller = controller;
}
/**
* {@inheritDoc}
*/
public ClassLoader getClassLoader()
{
return m_classLoader;
}
/**
* {@inheritDoc}
*/
@Override
public long getId()
{
return -1;
}
/**
* {@inheritDoc}
*/
@Override
public OutputStream getOutputStream()
{
return m_classLoader.getContainerScope().getStandardInputOutputStream();
}
/**
* {@inheritDoc}
*/
@Override
public InputStream getInputStream()
{
return m_classLoader.getContainerScope().getStandardOutputInputStream();
}
/**
* {@inheritDoc}
*/
@Override
public InputStream getErrorStream()
{
return m_classLoader.getContainerScope().getStandardErrorInputStream();
}
/**
* {@inheritDoc}
*/
@Override
public int waitFor() throws InterruptedException
{
// here we simply try to wait for the application's start future
// to complete executing.
try
{
if (m_startFuture != null)
{
m_startFuture.get();
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public int exitValue()
{
return 0;
}
/**
* Starts the application.
*/
public void start()
{
if (m_controller == null)
{
m_startFuture = null;
}
else
{
m_startFuture = m_controller.start(this);
}
}
/**
* {@inheritDoc}
*/
@Override
public void destroy()
{
if (m_controller != null)
{
// now try to stop
try
{
Future> future = m_controller.destroy(this);
if (future != null)
{
future.get();
}
}
catch (Exception e)
{
// TODO: log this exception
e.printStackTrace();
}
m_controller = null;
}
// close the scope
m_classLoader.getContainerScope().close();
}
/**
* {@inheritDoc}
*/
@Override
public Future submit(final Callable callable)
{
if (m_controller == null)
{
throw new IllegalStateException("Attempting to submit to a ContainerBasedJavaProcess that has been destroyed");
}
else
{
final ClassLoader classLoader = m_classLoader;
Callable scopingCallable = new Callable()
{
@Override
public T call() throws Exception
{
// remember the current context ClassLoader of the thread
// (so that we can return it back to normal when we're finished executing)
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try
{
// set the context ClassLoader of the Thread to be that of the
// ContainerClassLoader
Thread.currentThread().setContextClassLoader(m_classLoader);
// and associate the Thread with the Scope in the Container
Container.associateThreadWith(m_classLoader.getContainerScope());
// then execute the Callable as usual
return callable.call();
}
finally
{
// afterwards dissociate the Thread from the Scope in the Container
Container.dissociateThread();
// and return the current context ClassLoader back to normal
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
}
};
return m_executorService.submit(scopingCallable);
}
}
}
/**
* An {@link ApplicationController} that will call specific methods to start
* and destroy a {@link ContainerBasedJavaProcess}.
*/
public static class CustomController implements ApplicationController
{
/**
* The {@link CallableStaticMethod} to start the application.
*/
private CallableStaticMethod> m_callableStartStaticMethod;
/**
* The {@link CallableStaticMethod} to destroy the application.
*/
private CallableStaticMethod> m_callableDestroyStaticMethod;
/**
* Constructs a CustomController.
*
* @param callableStartStaticMethod the {@link CallableStaticMethod} to
* start the application (may be null)
*/
public CustomController(CallableStaticMethod> callableStartStaticMethod)
{
this(callableStartStaticMethod, null);
}
/**
* Constructs a CustomController.
*
* @param callableStartStaticMethod the {@link CallableStaticMethod} to
* start the application (may be null)
* @param callableDestroyStaticMethod the {@link CallableStaticMethod} to
* destroy the application (may be null)
*
*/
public CustomController(CallableStaticMethod> callableStartStaticMethod,
CallableStaticMethod> callableDestroyStaticMethod)
{
m_callableStartStaticMethod = callableStartStaticMethod;
m_callableDestroyStaticMethod = callableDestroyStaticMethod;
}
/**
* {@inheritDoc}
*/
@Override
public Future> start(ControllableApplication application)
{
return m_callableStartStaticMethod == null ? null : application.submit(m_callableStartStaticMethod);
}
/**
* {@inheritDoc}
*/
@Override
public Future> destroy(ControllableApplication application)
{
return m_callableDestroyStaticMethod == null ? null : application.submit(m_callableDestroyStaticMethod);
}
}
/**
* An {@link ApplicationController} that does nothing to a
* {@link ContainerBasedJavaProcess}.
*/
public static class NullController implements ApplicationController
{
/**
* {@inheritDoc}
*/
@Override
public Future> start(ControllableApplication application)
{
// SKIP: there's nothing to do to the application
return null;
}
/**
* {@inheritDoc}
*/
@Override
public Future> destroy(ControllableApplication application)
{
// SKIP: there's nothing to do to the application
return null;
}
}
/**
* An {@link ApplicationController} that will start a regular Java
* Console application (that defines a standard Java main method).
*/
public static class StandardController implements ApplicationController
{
/**
* The name of the Application class that contains the main method.
*/
private String m_applicationClassName;
/**
* The arguments for the main method.
*/
private List m_arguments;
/**
* Constructs a StandardController.
*
* @param arguments the arguments for the main method
*/
public StandardController(String applicationClassName,
List arguments)
{
m_applicationClassName = applicationClassName;
m_arguments = arguments == null ? new ArrayList(0) : new ArrayList(arguments);
}
/**
* Obtains the name of the class that defines the application main method.
*
* @return the application class name
*/
public String getApplicationClassName()
{
return m_applicationClassName;
}
/**
* Obtains a copy of the arguments used to start the application.
*
* @return a list of arguments
*/
public List getArguments()
{
return new ArrayList(m_arguments);
}
/**
* {@inheritDoc}
*/
@Override
public Future> start(ControllableApplication application)
{
Callable callable = new CallableStaticMethod(m_applicationClassName, "main", m_arguments);
Future future = application.submit(callable);
return future;
}
/**
* {@inheritDoc}
*/
@Override
public Future> destroy(ControllableApplication application)
{
// SKIP: there's no standard method to stop and destroy a
// regular Java console application running in a container
return null;
}
}
}