
com.oracle.bedrock.runtime.concurrent.RemoteChannel Maven / Gradle / Ivy
/*
* File: RemoteChannel.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.bedrock.runtime.concurrent;
import com.oracle.bedrock.Option;
import com.oracle.bedrock.Options;
import com.oracle.bedrock.annotations.Experimental;
import com.oracle.bedrock.annotations.Internal;
import com.oracle.bedrock.runtime.concurrent.options.StreamName;
import java.io.Closeable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.CompletableFuture;
/**
* Provides a means of submitting {@link RemoteCallable}s and {@link RemoteRunnable}s for
* asynchronous remote execution, and asynchronously sending and processing
* {@link RemoteEvent}s.
*
* The submitted {@link RemoteCallable}s, {@link RemoteRunnable} and {@link RemoteEvent}s do not
* necessarily need to be {@link java.io.Serializable}.
*
* If they are, they will be serialized/deserialized using Java Serialization. If they are not,
* their class-names will be serialized (and then later deserialized when required).
*
* 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 interface RemoteChannel extends Closeable
{
/**
* An {@link Option} signifying what type of notification is
* required when raising events.
*/
enum AcknowledgeWhen implements Option
{
/**
* The {@link CompletableFuture} returned by the {@link RemoteChannel#raise(RemoteEvent, Option...)}
* will be completed when the event has been raised.
*/
@Options.Default
SENT,
/**
* The {@link CompletableFuture} returned by the {@link RemoteChannel#raise(RemoteEvent, Option...)}
* will be completed when the event has been processed by the remote listeners.
*/
PROCESSED
}
/**
* Submits a {@link RemoteCallable} for asynchronous execution by the
* {@link RemoteChannel}.
*
* @param callable the {@link RemoteCallable} to be executed
* @param options the {@link Option}s for the {@link RemoteCallable}
* @param the return type of the {@link RemoteCallable}
*
* @return a {@link CompletableFuture} that will be completed with the result
* of the {@link RemoteCallable} execution.
*
* @throws IllegalStateException if the {@link RemoteChannel} is closed or
* is unable to accept the submission
*/
public CompletableFuture submit(RemoteCallable callable,
Option... options) throws IllegalStateException;
/**
* Submits a {@link RemoteRunnable} for asynchronous execution by the
* {@link RemoteChannel}.
*
* @param runnable the {@link RemoteRunnable} to be executed
* @param options the {@link Option}s for the {@link RemoteRunnable}
*
* @return a {@link CompletableFuture} that will be completed when the
* {@link RemoteRunnable} is executed.
*
* @throws IllegalStateException if the {@link RemoteChannel} is closed or
* is unable to accept the submission
*/
public CompletableFuture submit(RemoteRunnable runnable,
Option... options) throws IllegalStateException;
/**
* Adds a {@link RemoteEventListener} to the {@link RemoteChannel} so that it
* can handle and process {@link RemoteEvent}s.
*
* The specified {@link Option}s allow customized {@link RemoteEventListener}
* processing. For example; processing only {@link RemoteEvent}s on
* particular "streams" is permitted by specifying the {@link StreamName} option.
*
* Note: {@link RemoteEventListener}s may be added before a {@link RemoteChannel}
* has been started.
*
* @param listener the {@link RemoteEventListener}
* @param options the {@link Option}s
*/
public void addListener(RemoteEventListener listener,
Option... options);
/**
* Removes a previously added {@link RemoteEventListener}.
*
* Note: {@link RemoteEventListener}s may be removed after a {@link RemoteChannel}
* has been stopped.
*
* @param listener the {@link RemoteEventListener} to remove
* @param options the {@link Option}s used to add the {@link RemoteEventListener}
*/
public void removeListener(RemoteEventListener listener,
Option... options);
/**
* Raise an {@link RemoteEvent} on the opposite end of the {@link RemoteChannel}
* for registered {@link RemoteEventListener}s using the provided {@link Option}s.
*
* The specified {@link Option}s allow customized {@link RemoteEvent} deliver.
* For example; delivering {@link RemoteEvent}s for particular "streams" is
* permitted by specifying the {@link StreamName} option.
*
* @param event the {@link RemoteEvent}
* @param options the {@link Option}s
*
* @return a {@link CompletableFuture} allowing an application to determine
* when the event has been raised
*/
public CompletableFuture raise(RemoteEvent event,
Option... options);
/**
* An abstract class with a single method to inject a {@link RemoteChannel}
* into static fields or static methods of other classes.
*/
@Experimental
abstract class Injector
{
/**
* Inject the specified {@link RemoteChannel} into static fields or static methods of
* the specified {@link Class}.
*
* @param targetClass the {@link Class} to have the {@link RemoteChannel} injected
* @param channel the {@link RemoteChannel} to inject
*/
public static void injectChannel(Class> targetClass,
RemoteChannel channel)
{
if (channel == null)
{
return;
}
try
{
ClassLoader loader = targetClass.getClassLoader();
Class annotation =
(Class) loader.loadClass(RemoteChannel.Inject.class.getName());
Class> channelClass = channel.getClass();
for (Method method : targetClass.getMethods())
{
int modifiers = method.getModifiers();
if (method.getAnnotation(annotation) != null
&& method.getParameterTypes().length == 1
&& method.getParameterTypes()[0].isAssignableFrom(channelClass)
&& Modifier.isStatic(modifiers)
&& Modifier.isPublic(modifiers))
{
try
{
method.invoke(null, channel);
}
catch (Exception e)
{
// carry on... perhaps we can use another approach?
}
}
}
for (Field field : targetClass.getFields())
{
int modifiers = field.getModifiers();
if (field.getAnnotation(annotation) != null
&& Modifier.isStatic(modifiers)
&& Modifier.isPublic(modifiers)
&& field.getType().isAssignableFrom(channelClass))
{
try
{
field.set(null, channel);
}
catch (Exception e)
{
// carry on... perhaps we can use another approach?
}
}
}
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
/**
* Defines how a {@link RemoteChannel} may be injected into a {@link Class}
* that requires a reference to a {@link RemoteChannel}. The target class is
* typically the main class that runs as an application.
*
* If this annotation is used on fields the fields should be public
* static and of type {@link RemoteChannel}.
*
* If this annotation is used on methods the methods should be public
* static and have a single parameter of type {@link RemoteChannel}.
*
*
* The {@link Inject} annotation can be used to specify a public static
* field to inject a {@link RemoteChannel} reference into.
*
* public class App {
* ...
* @RemoteChannel.Inject
* public static RemoteChannel CHANNEL;
* ...
* }
*
*
* Alternatively, the {@link Inject} annotation can be used to specify that
* the public static method is called for injecting the value.
*
* public class App {
* ...
* @RemoteChannel.Inject
* public static void setPublisher(RemoteChannel channel) {
* ...
* }
* ...
* }
*
*
* @see Injector#injectChannel(Class, RemoteChannel)
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
@interface Inject
{
}
}