/* * Copyright (C) 2011 Google Inc. * * 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 org.ros.internal.node.service; import com.google.common.base.Preconditions; import org.apache.commons.logging.Log; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.ChannelHandler; import org.ros.address.AdvertiseAddress; import org.ros.concurrent.ListenerGroup; import org.ros.concurrent.SignalRunnable; import org.ros.internal.message.service.ServiceDescription; import org.ros.internal.node.topic.DefaultPublisher; import org.ros.internal.transport.ConnectionHeader; import org.ros.internal.transport.ConnectionHeaderFields; import org.ros.log.RosLogFactory; import org.ros.message.MessageDeserializer; import org.ros.message.MessageFactory; import org.ros.message.MessageSerializer; import org.ros.namespace.GraphName; import org.ros.node.service.DefaultServiceServerListener; import org.ros.node.service.ServiceResponseBuilder; import org.ros.node.service.ServiceServer; import org.ros.node.service.ServiceServerListener; import java.net.URI; import java.util.concurrent.ScheduledExecutorService; /** * Default implementation of a {@link ServiceServer}. * * @author damonkohler@google.com (Damon Kohler) */ public class DefaultServiceServer<T, S> implements ServiceServer<T, S> { private static final Log log = RosLogFactory.getLog(DefaultPublisher.class); private final ServiceDeclaration serviceDeclaration; private final ServiceResponseBuilder<T, S> serviceResponseBuilder; private final AdvertiseAddress advertiseAddress; private final MessageDeserializer<T> messageDeserializer; private final MessageSerializer<S> messageSerializer; private final MessageFactory messageFactory; private final ScheduledExecutorService scheduledExecutorService; private final ListenerGroup<ServiceServerListener<T, S>> listenerGroup; public DefaultServiceServer(ServiceDeclaration serviceDeclaration, ServiceResponseBuilder<T, S> serviceResponseBuilder, AdvertiseAddress advertiseAddress, MessageDeserializer<T> messageDeserializer, MessageSerializer<S> messageSerializer, MessageFactory messageFactory, ScheduledExecutorService scheduledExecutorService) { this.serviceDeclaration = serviceDeclaration; this.serviceResponseBuilder = serviceResponseBuilder; this.advertiseAddress = advertiseAddress; this.messageDeserializer = messageDeserializer; this.messageSerializer = messageSerializer; this.messageFactory = messageFactory; this.scheduledExecutorService = scheduledExecutorService; listenerGroup = new ListenerGroup<ServiceServerListener<T, S>>(scheduledExecutorService); listenerGroup.add(new DefaultServiceServerListener<T, S>() { @Override public void onMasterRegistrationSuccess(ServiceServer<T, S> registrant) { log.info("Service registered: " + DefaultServiceServer.this); } @Override public void onMasterRegistrationFailure(ServiceServer<T, S> registrant) { log.info("Service registration failed: " + DefaultServiceServer.this); } @Override public void onMasterUnregistrationSuccess(ServiceServer<T, S> registrant) { log.info("Service unregistered: " + DefaultServiceServer.this); } @Override public void onMasterUnregistrationFailure(ServiceServer<T, S> registrant) { log.info("Service unregistration failed: " + DefaultServiceServer.this); } }); } public ChannelBuffer finishHandshake(ConnectionHeader incomingConnectionHeader) { if (log.isDebugEnabled()) { log.debug("Client handshake header: " + incomingConnectionHeader); } ConnectionHeader connectionHeader = toDeclaration().toConnectionHeader(); String expectedChecksum = connectionHeader.getField(ConnectionHeaderFields.MD5_CHECKSUM); String incomingChecksum = incomingConnectionHeader.getField(ConnectionHeaderFields.MD5_CHECKSUM); // TODO(damonkohler): Pull out header field comparison logic. Preconditions.checkState(incomingChecksum.equals(expectedChecksum) || incomingChecksum.equals("*")); if (log.isDebugEnabled()) { log.debug("Server handshake header: " + connectionHeader); } return connectionHeader.encode(); } @Override public URI getUri() { return advertiseAddress.toUri("rosrpc"); } @Override public GraphName getName() { return serviceDeclaration.getName(); } /** * @return a new {@link ServiceDeclaration} with this * {@link DefaultServiceServer}'s {@link URI} */ ServiceDeclaration toDeclaration() { ServiceIdentifier identifier = new ServiceIdentifier(serviceDeclaration.getName(), getUri()); return new ServiceDeclaration(identifier, new ServiceDescription(serviceDeclaration.getType(), serviceDeclaration.getDefinition(), serviceDeclaration.getMd5Checksum())); } public ChannelHandler newRequestHandler() { return new ServiceRequestHandler<T, S>(serviceDeclaration, serviceResponseBuilder, messageDeserializer, messageSerializer, messageFactory, scheduledExecutorService); } /** * Signal all {@link ServiceServerListener}s that the {@link ServiceServer} * has been successfully registered with the master. * * <p> * Each listener is called in a separate thread. */ public void signalOnMasterRegistrationSuccess() { final ServiceServer<T, S> serviceServer = this; listenerGroup.signal(new SignalRunnable<ServiceServerListener<T, S>>() { @Override public void run(ServiceServerListener<T, S> listener) { listener.onMasterRegistrationSuccess(serviceServer); } }); } /** * Signal all {@link ServiceServerListener}s that the {@link ServiceServer} * has failed to register with the master. * * <p> * Each listener is called in a separate thread. */ public void signalOnMasterRegistrationFailure() { final ServiceServer<T, S> serviceServer = this; listenerGroup.signal(new SignalRunnable<ServiceServerListener<T, S>>() { @Override public void run(ServiceServerListener<T, S> listener) { listener.onMasterRegistrationFailure(serviceServer); } }); } /** * Signal all {@link ServiceServerListener}s that the {@link ServiceServer} * has been successfully unregistered with the master. * * <p> * Each listener is called in a separate thread. */ public void signalOnMasterUnregistrationSuccess() { final ServiceServer<T, S> serviceServer = this; listenerGroup.signal(new SignalRunnable<ServiceServerListener<T, S>>() { @Override public void run(ServiceServerListener<T, S> listener) { listener.onMasterUnregistrationSuccess(serviceServer); } }); } /** * Signal all {@link ServiceServerListener}s that the {@link ServiceServer} * has failed to unregister with the master. * * <p> * Each listener is called in a separate thread. */ public void signalOnMasterUnregistrationFailure() { final ServiceServer<T, S> serviceServer = this; listenerGroup.signal(new SignalRunnable<ServiceServerListener<T, S>>() { @Override public void run(ServiceServerListener<T, S> listener) { listener.onMasterUnregistrationFailure(serviceServer); } }); } @Override public void shutdown() { listenerGroup.shutdown(); } @Override public void addListener(ServiceServerListener<T, S> listener) { listenerGroup.add(listener); } @Override public String toString() { return "ServiceServer<" + toDeclaration() + ">"; } }