package org.distributeme.registry.servlet;
import org.distributeme.core.RegistryUtil;
import org.distributeme.core.ServiceDescriptor;
import org.distributeme.registry.metaregistry.Cluster;
import org.distributeme.registry.metaregistry.MetaRegistry;
import org.distributeme.registry.metaregistry.MetaRegistryConfig;
import org.distributeme.registry.metaregistry.MetaRegistryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.List;
/**
* Servlet for the http interface of the meta registry.
* @author lrosenberg.
*/
@SuppressWarnings("serial")
public class MetaRegistryServlet extends BaseRegistryServlet{
/**
* Supported operations.
* @author lrosenberg.
*
*/
private static enum Operation{
/**
* Bind a service descriptor.
*/
BIND,
/**
* Unbind a service descriptor.
*/
UNBIND,
/**
* List of binded services.
*/
LIST,
/**
* Resolve a service operation.
*/
RESOLVE,
/**
* Notify bind.
*/
NBIND,
/**
* Notify unbind.
*/
NUNBIND,
/**
* Ping.
*/
PING,
/**
* Identify the id of this registry in the cluster.
*/
IDENTIFY,
};
/**
* Singleton instance.
*/
private static MetaRegistry registry = MetaRegistryImpl.getInstance();
/**
* Config.
*/
private static final MetaRegistryConfig config = MetaRegistryConfig.create();
/**
* The logger.
*/
private static Logger log = LoggerFactory.getLogger(MetaRegistryServlet.class);
@Override
protected void moskitoDoGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String op = getOperation(req);
//NOTE this can throw an illegal argument exception, but since this servlet will never be exposed, its ok, and there
//is no further error handling needed.
Operation operation = Operation.valueOf(op.toUpperCase());
switch(operation){
case BIND:
bind(req, res);
break;
case UNBIND:
unbind(req, res);
break;
case LIST:
list(req, res);
break;
case RESOLVE:
resolve(req, res);
break;
case IDENTIFY:
identify(req, res);
break;
case PING:
ping(req, res);
break;
case NBIND:
notifyBind(req, res);
break;
case NUNBIND:
notifyUnbind(req, res);
break;
default:
throw new IllegalArgumentException("Parseable but (yet) unsupported operation.");
}
}
/**
* Processes bind request.
* @param req the http servlet request object.
* @param res the http servlet response object.
*/
private void bind(HttpServletRequest req, HttpServletResponse res){
String registrationId = getId(req);
ServiceDescriptor toBind = ServiceDescriptor.fromRegistrationString(registrationId);
boolean success = registry.bind(toBind);
sendBooleanResponse(res, success);
}
/**
* Processes unbind request.
* @param req the http servlet request object.
* @param res the http servlet response object.
*/
private void unbind(HttpServletRequest req, HttpServletResponse res){
String registrationId = getId(req);
ServiceDescriptor toUnBind = ServiceDescriptor.fromRegistrationString(registrationId);
boolean success = registry.unbind(toUnBind);
sendBooleanResponse(res, success);
}
/**
* Processes resolve request.
* @param req the http servlet request object.
* @param res the http servlet response object.
*/
private void resolve(HttpServletRequest req, HttpServletResponse res){
String serviceId = getId(req);
ServiceDescriptor toResolve = ServiceDescriptor.fromResolveString(serviceId);
ServiceDescriptor service = registry.resolve(serviceId);
if (service==null) {
if (config.isRegistryParentLookup()) {
service = RegistryUtil.resolve(toResolve, config);
if (service == null) {
sendBooleanResponse(res, false);
} else {
sendResponse(res, service.getRegistrationString());
}
} else {
sendBooleanResponse(res, false);
}
} else
sendResponse(res, service.getRegistrationString());
}
//yes, this isn't nice, but it had to be worked out fast.
/**
* Processes list request.
* @param req the http servlet request object.
* @param res the http servlet response object.
*/
private void list(HttpServletRequest req, HttpServletResponse res){//NOPMD
StringBuilder m = new StringBuilder();
m.append("<?xml version=\"1.0\"?>");
m.append("<services>");
List<ServiceDescriptor> bindinds = registry.list();
for (ServiceDescriptor service : bindinds){
m.append("<service serviceId=\"").append(service.getServiceId()).append("\" host=\"").append(service.getHost()).append("\"");
m.append(" port =\"").append(service.getPort()).append("\"");
m.append(" protocol =\"").append(service.getProtocol()).append("\"");
m.append(" instanceId =\"").append(service.getInstanceId()).append("\"");
m.append(" globalId =\"").append(service.getGlobalServiceId()).append("\"");
m.append(" registrationString =\"").append(service.getRegistrationString()).append("\"");
m.append("/>");
}
m.append("</services>");
String message = m.toString();
byte[] messageBytes = message.getBytes(Charset.forName("UTF-8"));
res.setContentLength(messageBytes.length);
res.setContentType("text/xml");
OutputStream out = null;
try{
out = res.getOutputStream();
out.write(messageBytes);
out.flush();
}catch(IOException e){
log.warn("sendResponse(res, "+message+")", e);
}finally{
if (out!=null){
try{
out.close();
}catch(IOException ignored){//NOPMD
}
}
}
}
/**
* Sends internal cluster identification to the output. This is used to determine which instance of the cluster is one himself.
* @param req
* @param res
*/
private void identify(HttpServletRequest req, HttpServletResponse res){
log.debug("incoming identify");
sendResponse(res, Cluster.INSTANCE.getId());
}
private void ping(HttpServletRequest req, HttpServletResponse res){
log.debug("incoming ping");
sendResponse(res, Cluster.INSTANCE.getId());
}
/**
* Called as notification on a bind from another registry.
* @param req
* @param res
*/
private void notifyBind(HttpServletRequest req, HttpServletResponse res){
String registrationId = getId(req);
log.debug("Notified about remote bind (registration) "+registrationId);
ServiceDescriptor toBind = ServiceDescriptor.fromRegistrationString(registrationId);
registry.remoteBind(toBind);
sendBooleanResponse(res, Boolean.TRUE);
}
/**
* Called as notification on an unbind from another registry.
* @param req
* @param res
*/
private void notifyUnbind(HttpServletRequest req, HttpServletResponse res){
String registrationId = getId(req);
log.debug("Notified about remote unbind (deregistration) "+registrationId);
ServiceDescriptor toUnBind = ServiceDescriptor.fromRegistrationString(registrationId);
registry.remoteUnbind(toUnBind);
sendBooleanResponse(res, Boolean.TRUE);
}
}