/* Copyright (c) 2011 Danish Maritime Authority.
*
* 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.maritimecloud.internal.mms.client.endpoint;
import static java.util.Objects.requireNonNull;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import net.maritimecloud.core.id.MaritimeId;
import net.maritimecloud.internal.mms.client.ClientInfo;
import net.maritimecloud.internal.mms.client.MmsThreadManager;
import net.maritimecloud.internal.mms.client.connection.ClientConnection;
import net.maritimecloud.internal.mms.messages.services.Services;
import net.maritimecloud.internal.net.endpoint.EndpointManager;
import net.maritimecloud.internal.net.endpoint.EndpointMirror;
import net.maritimecloud.internal.net.messages.MethodInvoke;
import net.maritimecloud.internal.net.messages.MethodInvokeResult;
import net.maritimecloud.internal.net.util.DefaultEndpointInvocationFuture;
import net.maritimecloud.message.Message;
import net.maritimecloud.message.MessageSerializer;
import net.maritimecloud.message.ValueSerializer;
import net.maritimecloud.net.EndpointImplementation;
import net.maritimecloud.net.EndpointInvocationFuture;
import net.maritimecloud.net.EndpointRegistration;
import net.maritimecloud.net.LocalEndpoint;
import net.maritimecloud.net.mms.MmsClient;
import net.maritimecloud.net.mms.MmsClientClosedException;
import net.maritimecloud.net.mms.MmsEndpointLocator;
import net.maritimecloud.util.Binary;
import net.maritimecloud.util.Timestamp;
import net.maritimecloud.util.geometry.PositionTime;
/**
*
* @author Kasper Nielsen
*/
public class ClientEndpointManager {
final ClientInfo clientInfo;
final ClientConnection connection;
/** A map of subscribers. ChannelName -> List of listeners. */
final EndpointManager em = new EndpointManager();
/** The client container. */
final ConcurrentHashMap<Binary, RemoteInvocation> invokers = new ConcurrentHashMap<>();
final MmsThreadManager threadManager;
/**
* Creates a new instance of this class.
*
* @param connection
* the client connection
* @param threadManager
* the thread manager
* @param clientInfo
* clientInfo
*/
public ClientEndpointManager(ClientConnection connection, MmsThreadManager threadManager, ClientInfo clientInfo) {
this.connection = requireNonNull(connection);
this.threadManager = requireNonNull(threadManager);
this.clientInfo = requireNonNull(clientInfo);
connection.subscribe(MethodInvoke.class, (a, e) -> onMethodInvoke(e));
connection.subscribe(MethodInvokeResult.class, (a, e) -> onMethodInvokeResult(e));
}
public <T extends LocalEndpoint> MmsEndpointLocator<T> endpointFind(Class<T> endpointType) {
return new DefaultEndpointLocator<>(this, EndpointMirror.from(endpointType), Integer.MAX_VALUE);
}
/**
* Creates a ServiceLocator for a service of the specified type.
*
* @param id
* the id to create an endpoint from
* @param endpointType
* the type of endpoint to create
* @param <T>
* the type of endpoint
* @return a service locator object
*
* @throws MmsClientClosedException
* if the connection has been permanently closed
*/
@SuppressWarnings("unchecked")
public <T extends LocalEndpoint> T endpointFrom(MaritimeId id, Class<? extends T> endpointType) {
EndpointMirror m = EndpointMirror.from(endpointType);
return (T) m.instantiate(new DefaultEndpointInvocator(this, id));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> EndpointInvocationFuture<T> invokeRemote(MaritimeId receiver, String endpoint, Message parameters,
MessageSerializer<? extends Message> serializer, ValueSerializer<T> resultParser) {
requireNonNull(endpoint, "endpoint is null");
requireNonNull(parameters, "parameters is null");
MethodInvoke ei = new MethodInvoke();
ei.setMessageId(Binary.random(32));
ei.setEndpointMethod(endpoint);
// Vi skal have en serializer med i metoden
ei.setParameters(MessageSerializer.writeToJSON(parameters, (MessageSerializer) serializer));
if (receiver != null) {
ei.setReceiverId(receiver.toString());
}
ei.setSenderTimestamp(Timestamp.now());
Optional<PositionTime> r = clientInfo.getCurrentPosition();
if (r.isPresent()) {
ei.setSenderPosition(r.get());
}
ei.setSenderId(clientInfo.getClientId().toString());
DefaultEndpointInvocationFuture<T> result = threadManager.create(ei.getMessageId());
result.recivedByCloud = connection.sendMessage(ei);
invokers.put(ei.getMessageId(), new RemoteInvocation(result, resultParser));
return result;
}
/**
* Registers the specified endpoint with the maritime cloud. If a client is closed via {@link MmsClient#shutdown()} the
* server will automatically deregister all endpoints.
*
* @param implementation
* the endpoint implementation
* @return an endpoint registration object
*
* @throws MmsClientClosedException
* if the connection has been permanently closed
*/
public EndpointRegistration endpointRegister(EndpointImplementation implementation) {
em.endpointRegister(implementation);
Services s = endpointFrom(null, Services.class);
DefaultEndpointRegistration reg = new DefaultEndpointRegistration();
s.registerEndpoint(implementation.getEndpointName()).thenRun(() -> reg.replied.countDown());
return reg;
}
void onMethodInvoke(MethodInvoke message) {
em.execute(message, e -> connection.sendMessage(e));
}
void onMethodInvokeResult(MethodInvokeResult m) {
RemoteInvocation f = invokers.get(m.getResultForMessageId());
if (f != null) {
f.complete(m);
}
}
}