blob: 2f362725443d7cc67d77383c5444dca7340072b1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.vinci.transport;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import org.apache.vinci.debug.Debug;
import org.apache.vinci.transport.context.VinciContext;
import org.apache.vinci.transport.vns.client.ServeonResult;
/**
* "Standard" service container for a VinciServable. This extends BaseServer with functions allowing
* port negotiation via interaction with VNS. It also provides rudimentary support for service
* control and monitoring by responding to vinci:SHUTDOWN and vinci:PING.
*
* Note that this server class directs requests to a single VinciServable. For many applications it
* may be desirable to have one server accept requests that get delegated to multiple services, not
* just a single service. For such applications consider using the MultiplexedServer. You can also
* implement a VinciServable that implements its own sub-service delegation scheme.
*
*/
public class VinciServer extends BaseServer {
private int priority = 0;
private int instance = 0;
private String hostName = null;
private String serviceName = null;
// Port gets set via VNS port negotiation once serving
// is initiated.
private volatile int port = -1;
private VinciContext context = null;
/**
* Create a new server. If an incorrect hostname is provided, this server will not be reachable.
*
* @param host_name
* The DNS hostname of the machine running this server.
* @param service_name -
* @param servable -
*
* @pre service_name != null
* @pre host_name != null
* @pre servable != null
*/
public VinciServer(String service_name, String host_name, VinciServable servable) {
this(service_name, host_name, servable, 0);
}
/**
* Create a new server that reports the current machine's IP address as the host. This should not
* be used for DHCP-based hosts since IP address can change.
*
* @param service_name -
* @param servable -
* @throws UnknownHostException
* If there is an error determining machine IP address.
*
* @pre service_name != null
* @pre servable != null
*/
public VinciServer(String service_name, VinciServable servable) throws UnknownHostException {
this(service_name, InetAddress.getLocalHost().getHostAddress(), servable);
}
/**
* Create a new server.
*
* @pre service_name != null
* @pre host_name != null
* @pre servable != null
* @pre myPriority ≥ -1
* @pre myInstance ≥ 0
* @param service_name -
* @param host_name -
* @param servable -
* @param myPriority -
* @param myInstance -
*/
public VinciServer(String service_name, String host_name, VinciServable servable, int myPriority,
int myInstance) {
super(servable);
this.priority = myPriority;
this.serviceName = service_name;
this.instance = myInstance;
this.hostName = host_name;
}
/**
* @pre service_name != null
* @pre host_name != null
* @pre servable != null
* @pre myPriority ≥ -1
* @param service_name -
* @param host_name -
* @param servable -
* @param myPriority -
*/
public VinciServer(String service_name, String host_name, VinciServable servable, int myPriority) {
this(service_name, host_name, servable, myPriority, 0);
}
/**
* @pre service_name != null
* @pre servable != null
* @pre myPriority ≥ -1
* @param service_name -
* @param servable -
* @param myPriority -
* @throws UnknownHostException -
*/
public VinciServer(String service_name, VinciServable servable, int myPriority)
throws UnknownHostException {
this(service_name, InetAddress.getLocalHost().getHostAddress(), servable, myPriority);
}
/**
* @pre service_name != null
* @pre servable != null
* @pre myPriority ≥ -1
* @pre myInstance ≥ 0
* @param service_name -
* @param servable -
* @param myPriority -
* @param myInstance -
* @throws UnknownHostException -
*/
public VinciServer(String service_name, VinciServable servable, int myPriority, int myInstance)
throws UnknownHostException {
this(service_name, InetAddress.getLocalHost().getHostName(), servable, myPriority, myInstance);
}
/**
* servable != null
* @param servable -
*/
public VinciServer(VinciServable servable) {
super(servable);
}
/**
* Get the context associated with this server. By default clients use the global Vinci context,
* though this can be overridden.
* @return -
*/
public VinciContext getContext() {
if (context == null) {
return VinciContext.getGlobalContext();
} else {
return context;
}
}
public int getPriority() {
return this.priority;
}
public String getServiceName() {
return this.serviceName;
}
public int getInstance() {
return this.instance;
}
public String getHostName() {
return this.hostName;
}
/**
* After invoking serve() or startServing(), this method can be used to determine the port which
* was negotiated with VNS on which to serve requests.
*
* @since 2.0.15
* @return -
*/
public int getServingPort() {
return this.port;
}
/**
* Set the VinciContext to be used by this server. Set to null if you wish the global context to
* be used.
* @param c -
*/
public void setContext(VinciContext c) {
context = c;
}
/**
* Serve requests until a clean shutdown is triggered. Note that all three exceptions thrown by
* this method are IOExceptions so a single IOException catch phrase is sufficient unless it is
* important to determine the particular failure.
*
* @throws ServiceDownException
* Thrown if there was a failure to contact VNS for port negotiation.
* @throws VNSException
* Typically thrown if VNS does not recognize the service provided by this server.
* @throws IOException
* Thrown if there was some problem with the server socket.
* @throws IllegalStateException
* if VNS host is not specified.
*/
public void serve() throws ServiceDownException, VNSException, IOException {
this.port = getPort();
Debug.printDebuggingMessage("Service " + serviceName + " starting on port " + port);
serve(this.port);
}
/**
* Start a new thread that will serve requests until a clean shutdown is triggered. Note that all
* three exceptions thrown by this method are IOExceptions so a single IOException catch phrase is
* sufficient unless it is important to determine the particular failure. If this method returns
* without throwing an exception then the port has been determined and a new thread has been
* launched.
*
* @throws ServiceDownException
* Thrown if there was a failure to contact VNS for port negotiation.
* @throws VNSException
* Typically thrown if VNS does not recognize the service provided by this server.
* @throws IOException
* Thrown if there was some problem with the server socket.
* @throws IllegalStateException
* if VNS host is not specified.
* @since 2.0.15
*/
public void startServing() throws ServiceDownException, VNSException, IOException {
this.port = getPort();
Debug.p("Service " + serviceName + " asynchronously starting on port " + port);
startServing(this.port);
}
/**
* @pre client != null
*/
protected Runnable getRunnable(Socket client) {
return new VinciServerRunnable(client, this);
}
/**
* Factory method for creating a shutdown message. Send the returned object to any server, and if
* it is programmed to respond to shutdown, it will do so.
*
* @param shutdown_message
* Should be used to pass a message explaining the shutdown, or in the future it may also
* include authentication information for password-protected shutdown.
* @param shutdown_message -
* @return -
*/
public static Transportable createShutdownCommand(String shutdown_message) {
return new VinciFrame().fadd(TransportConstants.SHUTDOWN_KEY, shutdown_message);
}
/**
* This method is called by the server when a remote shutdown request is received. In general if
* you want to stop the server call shutdownServing() -- this method should have probably been
* declared "protected". You can override this method if you want the shutdown message to be
* ignored in certain cases.
* @param shutdown_message -
* @return -
*/
public boolean shutdown(String shutdown_message) {
// Override this method if shutdown is to be ignored in certain cases (by returning
// false). Default implementation unconditionally halts serving,
// and hence always returns true.
Debug.printDebuggingMessage("VinciServer.shutdown()", "Accepted shutdown request: "
+ shutdown_message);
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// shouldn't happen
Debug.reportException(e);
}
shutdownServing();
}
}).start();
return true;
}
/**
* @return -
* @throws ServiceDownException -
* @throws VNSException -
* @throws IllegalStateException
* if VNS host isn't specified.
*/
protected int getPort() throws ServiceDownException, VNSException {
try {
VinciContext myContext = getContext();
ServeonResult response = (ServeonResult) VinciClient.sendAndReceive(ServeonResult
.composeQuery(serviceName, hostName, priority, instance), myContext.getVNSHost(),
myContext.getVNSPort(), ServeonResult.factory, myContext.getVNSServeonTimeout());
return response.port;
} catch (IOException e) {
Debug.reportException(e);
throw new ServiceDownException("VNS inaccessible: " + e);
} catch (ServiceException e) {
throw new VNSException(e.getMessage());
}
}
}