All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.openhft.chronicle.wire.channel.ChronicleContext Maven / Gradle / Ivy

There is a newer version: 2.27ea1
Show newest version
/*
 * Copyright 2016-2022 chronicle.software
 *
 *       https://chronicle.software
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.openhft.chronicle.wire.channel;

import net.openhft.affinity.AffinityLock;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.core.io.Closeable;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.InvalidMarshallableException;
import net.openhft.chronicle.core.io.SimpleCloseable;
import net.openhft.chronicle.core.util.WeakIdentityHashMap;
import net.openhft.chronicle.wire.QueryWire;
import net.openhft.chronicle.wire.channel.impl.SocketRegistry;
import net.openhft.chronicle.wire.channel.impl.internal.Handler;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Set;
import java.util.function.Consumer;

import static net.openhft.chronicle.wire.WireType.YAML;

/**
 * This class encapsulates the context for a Chronicle channel, including parameters necessary for creating
 * and managing a channel, such as the socket registry, gateway, and system context.
 * The context can also manage the lifecycle of other closeable resources.
 *
 * 

The ChronicleContext provides methods to set important parameters such as the context's URL, whether * to use buffering and affinity, and the context's name (which can define the relative directory). * It also manages the lifecycle of closeable resources and provides the URL, buffering state, and the socket registry. * *

Example usage: *

 * String url = "tcp://:0";
 * try (ChronicleContext context = ChronicleContext.newContext(url)
 *     .name("target/server")
 *     .buffered(true)
 *     .useAffinity(true)) {
 *   ChronicleChannel channel = context.newChannelSupplier(new EchoHandler().buffered(false)).connectionTimeoutSecs(1).get();
 *   Says says = channel.methodWriter(Says.class);
 *   says.say("Hello World");
 *   StringBuilder eventType = new StringBuilder();
 *   String text = channel.readOne(eventType, String.class);
 *   assertEquals("say: Hello World", eventType + ": " + text);
 * 
*/ @SuppressWarnings("deprecation") public class ChronicleContext extends SimpleCloseable { static { // Initialize Handler at static context Handler.init(); } // The set to manage all the closeable resources private transient final Set closeableSet = Collections.synchronizedSet( Collections.newSetFromMap( new WeakIdentityHashMap<>())); // URL for the Chronicle context private final String url; private String name; @SuppressWarnings("deprecation") private transient URL _url; // Socket Registry for handling socket related operations private transient SocketRegistry socketRegistry; private boolean buffered; private boolean useAffinity; private ChronicleGatewayMain gateway; private SystemContext systemContext; private boolean privateSocketRegistry; private Consumer closeCallback; /** * Protected constructor for creating a Chronicle context with the specified URL. * * @param url the URL for this context */ protected ChronicleContext(String url) { this(url, null); } /** * Protected constructor for creating a Chronicle context with the specified URL and socket registry. * * @param url the URL for this context * @param socketRegistry the socket registry for this context */ @SuppressWarnings("this-escape") protected ChronicleContext(String url, SocketRegistry socketRegistry) { this.url = url; this.socketRegistry = socketRegistry; init(); } /** * Factory method for a new ChronicleContext * * @param url to connect to * @return the new ChronicleContext */ public static ChronicleContext newContext(String url) { return new ChronicleContext(url); } /** * Parses a URL from a string, initializing any custom handlers as necessary. * *

This method supports "internal:" and "tcp:" URL schemas in addition to the standard schemas. * If a URL starts with "internal:", a new Handler object is used as the URLStreamHandler. * If a URL starts with "tcp:", a new tcp.Handler object is used as the URLStreamHandler. * For other URL schemas, no custom URLStreamHandler is used. * * @param spec the string to parse as a URL. * @return the URL parsed from the string. * @throws IORuntimeException if the string cannot be parsed as a URL. */ @SuppressWarnings("deprecation") public static URL urlFor(String spec) throws IORuntimeException { try { if (spec.startsWith("internal:")) return new URL(null, spec, new Handler()); if (spec.startsWith("tcp:")) return new URL(null, spec, new net.openhft.chronicle.wire.channel.impl.tcp.Handler()); return new URL(spec); } catch (MalformedURLException e) { throw new IORuntimeException(e); } } /** * @return an AffinityLock appropriate for this context * Acquires an AffinityLock instance based on the affinity usage status of the context. If affinity usage is enabled, * a lock is acquired without a specific tag. If affinity usage is disabled, a lock is acquired with a null tag. */ public AffinityLock affinityLock() { return useAffinity() ? AffinityLock.acquireLock() : AffinityLock.acquireLock((String) null); } /** * Retrieves the status of affinity usage in this context. * * @return true if affinity usage is enabled, false otherwise. */ public boolean useAffinity() { return useAffinity; } /** * Sets the status of affinity usage in this context. * * @param useAffinity a boolean flag indicating whether to enable affinity usage. * @return the current ChronicleContext instance, allowing for method chaining. */ public ChronicleContext useAffinity(boolean useAffinity) { this.useAffinity = useAffinity; return this; } /** * Initializes the context, specifically it creates a new SocketRegistry instance if one is not already set. */ protected void init() { if (socketRegistry == null) { socketRegistry = new SocketRegistry(); privateSocketRegistry = true; } } /** * Constructs a new ChronicleChannelSupplier object using the provided ChannelHandler and the settings from this context. * * @param handler the ChannelHandler to be used by the new ChronicleChannelSupplier. * @return the newly created ChronicleChannelSupplier. * @throws InvalidMarshallableException if there is an error during the validation of the header. */ public ChronicleChannelSupplier newChannelSupplier(ChannelHandler handler) throws InvalidMarshallableException { startServerIfNeeded(); final ChronicleChannelSupplier connectionSupplier = new ChronicleChannelSupplier(this, handler); final String hostname = url().getHost(); final int port = port(); String query = url().getQuery(); String connectionId = null; if (query != null) { QueryWire wire = new QueryWire(Bytes.from(query)); connectionId = wire.read("sessionName").text(); } connectionSupplier .protocol(url().getProtocol()) .addHostnamePort(hostname == null || hostname.isEmpty() ? "localhost" : hostname, port) .connectionId(connectionId) .buffered(buffered()) .initiator(true); return connectionSupplier; } private void startServerIfNeeded() throws InvalidMarshallableException { if (url().getProtocol().equals("tcp") && "".equals(url().getHost())) { startNewGateway(); } } /** * Starts a new instance of ChronicleGatewayMain if one is not already instantiated for this context. * Configures the new gateway with the context's current parameters and adds it to the list of * closeable resources managed by the context. * * @throws InvalidMarshallableException if there's an error during the creation or starting of the gateway. */ public synchronized void startNewGateway() throws InvalidMarshallableException { // If gateway already exists, don't start a new one if (gateway != null) return; gateway = new ChronicleGatewayMain(url, socketRegistry, systemContext()); gateway.name(name()) .buffered(buffered()) .useAffinity(useAffinity()); try { // Add the gateway to the set of closeable resources addCloseable(gateway); // Start the gateway gateway.start(); } catch (IOException e) { throw new IORuntimeException(e); } } /** * Adds a Closeable resource to the context's internal set of resources. * This resource will be closed when the context itself is closed. * * @param closeable the Closeable resource to add. */ public void addCloseable(Closeable closeable) { closeableSet.add(closeable); } /** * Closes all resources managed by this context in a quiet manner, i.e., without throwing any exceptions. * After closing, the set of Closeable resources is cleared. If the SocketRegistry was privately * created by this context, it is also closed. */ protected void performClose() { Closeable.closeQuietly(closeableSet); closeableSet.clear(); if (privateSocketRegistry) socketRegistry.close(); } /** * Retrieves the URL of this context. If the URL hasn't been initialized yet, this method initializes it. * * @return the URL for this context. */ public URL url() { if (_url == null) _url = urlFor(url); return _url; } /** * Indicates whether buffering should be used in this context. * * @return true if buffering should be used, false otherwise. */ public boolean buffered() { return buffered; } /** * Sets the buffering preference for this context. If true, buffering will be used. * * @param buffered a boolean representing the preference for buffering. * @return this instance of ChronicleContext for method chaining. */ public ChronicleContext buffered(boolean buffered) { this.buffered = buffered; return this; } /** * Retrieves the SocketRegistry associated with this context. * * @return the {@link SocketRegistry} for this context. */ public SocketRegistry socketRegistry() { return socketRegistry; } /** * Sets the system context for this context. The given system context will be deep copied to prevent * modifications to the original SystemContext from affecting this context. * * @param systemContext the new system context to be set. * @throws InvalidMarshallableException if there's an error while performing a deep copy of the system context. */ public void systemContext(SystemContext systemContext) throws InvalidMarshallableException { this.systemContext = systemContext.deepCopy(); } /** * Retrieves the current SystemContext for this process or remote host. If the system context is not set, * a new SystemContext instance is created, deep copied, and returned. * * @return the current {@link SystemContext} for this process or remote host. */ public SystemContext systemContext() { return systemContext == null ? SystemContext.INSTANCE.deepCopy() : systemContext; } /** * Sets the names space for the service. This can set the relative directory for example. * * @param name space of this context * @return this */ public ChronicleContext name(String name) { this.name = name; return this; } /** * @return the name space of this context */ public String name() { return name; } /** * Returns a new File instance for the given name space (sub-directory) within this context. * * @param name the name for the new File * @return a new File instance */ public File toFile(String name) { if (this.name() == null) return new File(name); return new File(name(), name); } /** * @return the context in a YAML format */ @Override public String toString() { return YAML.asString(this); } /** * @return the port for the gateway, if it exists, otherwise the port from the URL */ public int port() { return gateway == null ? url().getPort() : gateway.port(); } /** * @param closeCallback a callback that you can provide with will be called in the TCP Channel is closed */ public void closeCallback(Consumer closeCallback) { this.closeCallback = closeCallback; } /** * @return this callback will be called in the TCP Channel is closed */ public Consumer closeCallback() { return this.closeCallback; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy