| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package io.v.v23; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.io.ByteStreams; |
| import com.google.common.io.Resources; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import io.v.impl.google.rt.VRuntimeImpl; |
| import io.v.v23.context.VContext; |
| import io.v.v23.namespace.Namespace; |
| import io.v.v23.rpc.Client; |
| import io.v.v23.rpc.Dispatcher; |
| import io.v.v23.rpc.ListenSpec; |
| import io.v.v23.rpc.Server; |
| import io.v.v23.security.Authorizer; |
| import io.v.v23.security.CaveatRegistry; |
| import io.v.v23.security.ConstCaveatValidator; |
| import io.v.v23.security.Constants; |
| import io.v.v23.security.ExpiryCaveatValidator; |
| import io.v.v23.security.MethodCaveatValidator; |
| import io.v.v23.security.PublicKeyThirdPartyCaveatValidator; |
| import io.v.v23.security.VPrincipal; |
| import io.v.v23.verror.VException; |
| |
| /** |
| * The local environment allowing clients and servers to communicate with one another. The expected |
| * usage pattern of this class goes something like this: |
| * <p><blockquote><pre> |
| * VContext ctx = V.init(opts); |
| * ... |
| * ctx = V.withNewServer(ctx, "server", obj, null); |
| * ... |
| * Client c = V.getClient(ctx); |
| * ... |
| * </pre></blockquote><p> |
| */ |
| public class V { |
| private static native void nativeInit() throws VException; |
| private static native void nativeShutdown(VContext context); |
| |
| private static volatile VContext context = null; |
| private static volatile VRuntime runtime = null; |
| private static volatile boolean initOnceDone = false; |
| |
| private static boolean isDarwin() { |
| return System.getProperty("os.name").toLowerCase().contains("os x"); |
| } |
| |
| private static boolean isLinux() { |
| return System.getProperty("os.name").toLowerCase().contains("linux"); |
| } |
| |
| private static synchronized void initOnce() { |
| if (initOnceDone) { |
| return; |
| } |
| List<Throwable> errors = new ArrayList<Throwable>(); |
| try { |
| // First, attempt to find the library in java.library.path. |
| System.loadLibrary("v23"); |
| } catch (UnsatisfiedLinkError ule) { |
| // Thrown if the library does not exist. In this case, try to find it in our classpath. |
| errors.add(new RuntimeException("loadLibrary attempt failed", ule)); |
| try { |
| URL resource = null; |
| File file = null; |
| if (isLinux()) { |
| resource = Resources.getResource("libv23.so"); |
| file = File.createTempFile("libv23-", ".so"); |
| } else if (isDarwin()) { |
| resource = Resources.getResource("libv23.dylib"); |
| file = File.createTempFile("libv23-", ".dylib"); |
| } else { |
| String os = System.getProperty("os.name"); |
| errors.add(new RuntimeException("unsupported OS: " + os)); |
| throw new RuntimeException("Unsupported OS: " + os, new VLoaderException(errors)); |
| } |
| file.deleteOnExit(); |
| ByteStreams.copy(resource.openStream(), new FileOutputStream(file)); |
| System.load(file.getAbsolutePath()); |
| } catch (IllegalArgumentException iae) { |
| errors.add(new RuntimeException("couldn't locate libv23.so on the classpath", iae)); |
| throw new RuntimeException("Could not load v23 native library", new VLoaderException(errors)); |
| } catch (IOException e) { |
| errors.add(new RuntimeException("error while reading libv23.so from the classpath", e)); |
| throw new RuntimeException("Could not load v23 native library", new VLoaderException(errors)); |
| } catch (UnsatisfiedLinkError e) { |
| errors.add(new RuntimeException("error while reading libv23.so from the classpath", e)); |
| throw new RuntimeException("Could not load v23 native library", new VLoaderException(errors)); |
| } |
| } |
| try { |
| nativeInit(); |
| } catch (VException e) { |
| throw new RuntimeException("Could not initialize v23 native library", e); |
| } |
| |
| // Register caveat validators. |
| try { |
| CaveatRegistry.register( |
| io.v.v23.security.Constants.CONST_CAVEAT, |
| ConstCaveatValidator.INSTANCE); |
| CaveatRegistry.register( |
| io.v.v23.security.Constants.EXPIRY_CAVEAT, |
| ExpiryCaveatValidator.INSTANCE); |
| CaveatRegistry.register(io.v.v23.security.Constants.METHOD_CAVEAT, |
| MethodCaveatValidator.INSTANCE); |
| CaveatRegistry.register(Constants.PUBLIC_KEY_THIRD_PARTY_CAVEAT, |
| PublicKeyThirdPartyCaveatValidator.INSTANCE); |
| } catch (VException e) { |
| throw new RuntimeException("Couldn't register caveat validators", e); |
| } |
| |
| initOnceDone = true; |
| } |
| /** |
| * Initializes the Vanadium environment, returning the base context. Calling this method |
| * multiple times will always return the result of the first call to {@link #init init}, |
| * ignoring subsequently provided options, unless you first call {@link #shutdown}. |
| * <p> |
| * This method loads the native Vanadium implementation if it has not already been loaded. It |
| * searches for the native Vanadium library using {@link java.lang.System#loadLibrary}. |
| * If that throws, then the method will look for the library in the root of the classpath. |
| * If it is found, the bytes of the library are extracted to a temporary file and loaded with |
| * {@link java.lang.System#load}. |
| * <p> |
| * If the above procedure fails to load the native implementation, a {@link RuntimeException} |
| * will be thrown. The {@link RuntimeException#getCause cause} of the exception will be a |
| * {@link VLoaderException} indicating the exceptions that occurred while attempting to load |
| * the library. |
| * <p> |
| * A caller may pass the following option that specifies the runtime implementation to be used: |
| * <p><ul> |
| * <li>{@link OptionDefs#RUNTIME}</li> |
| * </ul><p> |
| * If this option isn't provided, the default runtime implementation is used. |
| * |
| * @param opts options |
| * @return base context |
| */ |
| public static VContext init(Options opts) { |
| if (context != null) return context; |
| synchronized (V.class) { |
| if (context != null) return context; |
| initOnce(); |
| if (opts == null) opts = new Options(); |
| // See if a runtime was provided as an option. |
| if (opts.get(OptionDefs.RUNTIME) != null) { |
| runtime = opts.get(OptionDefs.RUNTIME, VRuntime.class); |
| } else { |
| // Use the default runtime implementation. |
| try { |
| runtime = VRuntimeImpl.create(opts); |
| } catch (VException e) { |
| throw new RuntimeException("Couldn't initialize Google Vanadium Runtime", e); |
| } |
| } |
| context = runtime.getContext(); |
| |
| // Set the VException component name to this binary name. |
| context = VException.contextWithComponentName( |
| context, System.getProperty("program.name", "")); |
| return context; |
| } |
| } |
| |
| /** |
| * Initializes the Vanadium environment without options. See {@link #init(Options)} for more |
| * information. |
| * |
| * @return base context |
| */ |
| public static VContext init() { |
| return V.init(null); |
| } |
| |
| /** |
| * Shuts down the Vanadium environment. It is an error to call this method before calling |
| * {@link #init}, or more than once per call to {@link #init}. |
| * |
| * <p>After this call, you may initialize a new environment again by calling {@link #init}. |
| */ |
| public static void shutdown() { |
| synchronized (V.class) { |
| Preconditions.checkState(context != null, |
| "no context to shutdown, did you call init()?"); |
| runtime.shutdown(); |
| context = null; |
| runtime = null; |
| } |
| } |
| |
| /** |
| * Creates a new client instance and attaches it to a new context (which is derived from the |
| * provided context). |
| * |
| * @param ctx current context |
| * @return child context to which the new client is attached |
| * @throws VException if a new client cannot be created |
| */ |
| public static VContext withNewClient(VContext ctx) throws VException { |
| return withNewClient(ctx, null); |
| } |
| |
| /** |
| * Creates a new client instance with the provided options and attaches it to a new context |
| * (which is derived from the provided context). |
| * <p></p> |
| * A particular runtime implementation chooses which options to support, but at the minimum must |
| * handle the following options: |
| * <p><ul> |
| * <li>(CURRENTLY NO OPTIONS ARE MANDATED)</li> |
| * </ul> |
| * |
| * @param ctx current context |
| * @param opts client options |
| * @return child context to which the new client is attached |
| * @throws VException if a new client cannot be created |
| */ |
| public static VContext withNewClient(VContext ctx, Options opts) throws VException { |
| if (opts == null) opts = new Options(); |
| return getRuntime().withNewClient(ctx, opts); |
| } |
| |
| /** |
| * Returns the client attached to the given context, or {@code null} if no client is attached. |
| * <p> |
| * If the passed-in context is derived from the context returned by {@link #init}, the returned |
| * client will never be {@code null}. |
| */ |
| public static Client getClient(VContext ctx) { |
| return getRuntime().getClient(ctx); |
| } |
| |
| /** |
| * Creates a new {@link Server} instance to serve a service object and attaches |
| * it to a new context (which is derived from the provided context). |
| * |
| * The server will listen for network connections as specified by the {@link ListenSpec} |
| * attached to the context. Depending on your runtime, 'roaming' support may be enabled. |
| * In this mode the server will adapt to changes in the network configuration |
| * and re-publish the current set of endpoints to the mount table accordingly. |
| * <p> |
| * This call associates object with name by publishing the address of this server with the |
| * mount table under the supplied name and using the given authorizer to authorize access to it. |
| * RPCs invoked on the supplied name will be delivered to methods implemented by the supplied |
| * object. |
| * <p> |
| * Reflection is used to match requests to the object's method set. As a special-case, if the |
| * object implements the {@link io.v.v23.rpc.Invoker} interface, the invoker is used to invoke |
| * methods directly, without reflection. |
| * <p> |
| * If name is an empty string, no attempt will made to publish that name to a mount table. |
| * <p> |
| * If the passed-in authorizer is {@code null}, the default authorizer will be used. |
| * (The default authorizer uses the blessing chain derivation to determine if the client is |
| * authorized to access the object's methods.) |
| * |
| * @param ctx current context |
| * @param name name under which the supplied object should be published, |
| * or the empty string if the object should not be published |
| * @param object object to be published under the given name |
| * @param authorizer authorizer that will control access to objects methods |
| * @return a child context to which the new server is attached |
| * @throws VException if a new server cannot be created |
| */ |
| public static VContext withNewServer(VContext ctx, String name, Object object, |
| Authorizer authorizer) throws VException { |
| return withNewServer(ctx, name, object, authorizer, null); |
| } |
| |
| /** |
| * Creates a new {@link Server} instance to serve a service object and attaches |
| * it to a new context (which is derived from the provided context). |
| * |
| * Same as {@link #withNewServer(VContext, String, Object, Authorizer)} |
| * but accepts implementation-specific server-creation options. |
| * <p> |
| * A particular runtime implementation chooses which options to support, but at the minimum it |
| * must handle the following options: |
| * <p><ul> |
| * <li>(CURRENTLY NO OPTIONS ARE MANDATED)</li> |
| * </ul> |
| * |
| * @param ctx current context |
| * @param name name under which the supplied object should be published, |
| * or the empty string if the object should not be published |
| * @param object object to be published under the given name |
| * @param authorizer authorizer that will control access to objects methods |
| * @param opts server options |
| * @return a child context to which the new server is attached |
| * @throws VException if a new server cannot be created |
| */ |
| public static VContext withNewServer(VContext ctx, String name, Object object, |
| Authorizer authorizer, Options opts) throws VException { |
| if (opts == null) { |
| opts = new Options(); |
| } |
| return getRuntime().withNewServer(ctx, name, object, authorizer, opts); |
| } |
| |
| /** |
| * Creates a new {@link Server} instance to serve a dispatcher and attaches |
| * it to a new context (which is derived from the provided context). |
| * |
| * The server will listen for network connections as specified by the {@link ListenSpec} |
| * attached to the context. Depending on your runtime, 'roaming' support may be enabled. |
| * In this mode the server will adapt to changes in the network configuration |
| * and re-publish the current set of endpoints to the mount table accordingly. |
| * <p> |
| * Associates dispatcher with the portion of the mount table's name space for which |
| * {@code name} is a prefix, by publishing the address of this dispatcher with the mount |
| * table under the supplied name. |
| * <p> |
| * RPCs invoked on the supplied name will be delivered to the supplied {@link Dispatcher}'s |
| * {@link Dispatcher#lookup lookup} method which will in turn return the object and |
| * {@link Authorizer} used to serve the actual RPC call. |
| * <p> |
| * If name is an empty string, no attempt will made to publish that name to a mount table. |
| * |
| * @param ctx current context |
| * @param name name under which the supplied object should be published, |
| * or the empty string if the object should not be published |
| * @param dispatcher dispatcher to be published under the given name |
| * @return a child context to which the new server is attached |
| * @throws VException if a new server cannot be created |
| */ |
| public static VContext withNewServer(VContext ctx, String name, |
| Dispatcher dispatcher) throws VException { |
| return withNewServer(ctx, name, dispatcher, (Options) null); |
| } |
| |
| /** |
| * Creates a new {@link Server} instance to serve a dispatcher and attaches |
| * it to a new context (which is derived from the provided context). |
| * |
| * Same as {@link #withNewServer(VContext,String,Dispatcher)} but accepts |
| * implementation-specific server-creation options. |
| * <p> |
| * A particular runtime implementation chooses which options to support, |
| * but at the minimum it must handle the following options: |
| * <p><ul> |
| * <li>(CURRENTLY NO OPTIONS ARE MANDATED)</li> |
| * </ul> |
| * |
| * @param ctx current context |
| * @param name name under which the supplied object should be published, |
| * or the empty string if the object should not be published |
| * @param dispatcher dispatcher to be published under the given name |
| * @param opts server options |
| * @return a child context to which the new server is attached |
| * @throws VException if a new server cannot be created |
| */ |
| public static VContext withNewServer(VContext ctx, String name, Dispatcher dispatcher, |
| Options opts) throws VException { |
| if (opts == null) { |
| opts = new Options(); |
| } |
| return getRuntime().withNewServer(ctx, name, dispatcher, opts); |
| } |
| |
| /** |
| * Returns the server attached to the given context, or {@code null} if no server is attached. |
| */ |
| public static Server getServer(VContext ctx) { |
| return getRuntime().getServer(ctx); |
| } |
| |
| /** |
| * Attaches the given principal to a new context (which is derived from the provided context). |
| * |
| * @param ctx current context |
| * @param principal principal to be attached |
| * @return child context to which the principal is attached |
| * @throws VException if the principal couldn't be attached |
| */ |
| public static VContext withPrincipal(VContext ctx, VPrincipal principal) throws VException { |
| return getRuntime().withPrincipal(ctx, principal); |
| } |
| |
| /** |
| * Returns the principal attached to the given context or {@code null} if no principal is |
| * attached. |
| * <p> |
| * If the passed-in context is derived from the context returned by {@link #init}, the returned |
| * principal will never be {@code null}. |
| |
| */ |
| public static VPrincipal getPrincipal(VContext ctx) { |
| return getRuntime().getPrincipal(ctx); |
| } |
| |
| /** |
| * Creates a new namespace instance and attaches it to a new context (which is derived from the |
| * given context). |
| * |
| * @param ctx current context |
| * @param roots roots of the namespace |
| * @return child context to which the principal is attached |
| * @throws VException if the namespace couldn't be created |
| */ |
| public static VContext withNewNamespace(VContext ctx, String... roots) throws VException { |
| return getRuntime().withNewNamespace(ctx, roots); |
| } |
| |
| /** |
| * Returns the namespace attached to the given context, or {@code null} if no namespace is |
| * attached. |
| * <p> |
| * If the passed-in context is derived from the context returned by {@link #init}, the returned |
| * namespace will never be {@code null}. |
| */ |
| public static Namespace getNamespace(VContext ctx) { |
| return getRuntime().getNamespace(ctx); |
| } |
| |
| /** |
| * Attaches the given {@code ListenSpec} to a new context (which is derived from the given |
| * context). |
| * |
| * @param ctx current context |
| * @param spec the {@code ListenSpec} to attach |
| * @return child context to which the {@code ListenSpec} is attached. |
| */ |
| public static VContext withListenSpec(VContext ctx, ListenSpec spec) throws VException { |
| return getRuntime().withListenSpec(ctx, spec); |
| } |
| |
| /** |
| * Returns the {@code ListenSpec} attached to the given context, or {@code null} if no spec |
| * is attached. |
| * <p> |
| * If the passed-in context is derived from the context returned by {@link #init}, the returned |
| * spec will never be {@code null}. |
| */ |
| public static ListenSpec getListenSpec(VContext ctx) { |
| return getRuntime().getListenSpec(ctx); |
| } |
| |
| private static VRuntime getRuntime() { |
| init(null); |
| return runtime; |
| } |
| |
| protected V() {} |
| } |