blob: fb81f8ac895aca420b592e32c3006b5fa7be12e4 [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.vns.service;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.StringTokenizer;
import org.apache.vinci.debug.Debug;
import org.apache.vinci.transport.BaseClient;
import org.apache.vinci.transport.BaseServer;
import org.apache.vinci.transport.BaseServerRunnable;
import org.apache.vinci.transport.ErrorFrame;
import org.apache.vinci.transport.Frame;
import org.apache.vinci.transport.KeyValuePair;
import org.apache.vinci.transport.ServiceException;
import org.apache.vinci.transport.Transportable;
import org.apache.vinci.transport.VinciFrame;
import org.apache.vinci.transport.VinciServableAdapter;
import org.apache.vinci.transport.vns.VNSConstants;
/**
* VNS (Vinci Naming Service) provides the "directory" of all available Vinci services. VNS must be
* running somewhere on the network for VinciClient and VinciServer classes to function. These
* classes consult org.apache.vinci.transport.vns.client.VNSConfig to determine the location of the
* VNS service.
*/
public class VNS extends VinciServableAdapter {
private HashMap cachedResults = new HashMap();
public final static String dirCmdAddService = "addservice";
public final static String dirCmdAddAlias = "addalias";
public final static String dirCmdDelService = "delservice";
public final static String dirCmdDelAlias = "delalias";
public final static String dirCmdUpdateService = "updateservice";
public final static String dirCmdGetList = "getlist";
public final static String dirCmdGetNames = "getnames";
public final static String dirCmdGetHits = "gethits";
public String ENV_PROXY = "vinci.environment.proxy";
// Static variables set up during the start of the program
private static String configFile, backupFile, workspacesFile, counterFile;
private static String logFile = null;
private static String configDir = null;
private static int backupInterval = 120;
private static int srvPort = 9000;
private static boolean logFlag = false;
private static String bindAddress = null;
private static int backlog = 100;
private static int maxThreads = 200;
/* Instance variables */
int port;
String myLogFile = null;
Thread backupThread;
BackupThread backupThreadRunnable;
Hashtable hits; // for keeping track of the counters
int totalhits;
ServiceRegistry SR;
WorkspaceConfig WS;
Writer log; // for writing to the logfile
String starttime;
static File quitFile;
static private void setConfigDir(String path) {
if (path.charAt(path.length() - 1) == File.separatorChar) {
path = path.substring(0, path.length() - 1);
}
configDir = path;
configFile = path + File.separatorChar + "vns.services";
backupFile = path + File.separatorChar + "vns.services.bak";
workspacesFile = path + File.separatorChar + "vns.workspaces";
counterFile = path + File.separatorChar + "vns.counter";
quitFile = new File(configDir + File.separatorChar + "quit");
quitFile.delete();
}
public static void main(String[] args) throws IOException {
Debug.setThreadNameOutput(true);
System.setProperty("sun.net.inetaddr.ttl", "30");
// ^^ JDK1.3
java.security.Security.setProperty("networkaddress.cache.ttl", "30");
// ^^ JDK1.4
// ^^ REQUIRED to keep Java from caching "null" IP addresses for domain names
setConfigDir(".");
// Parse the commandline options
String s;
for (int i = 0; i < args.length; i++) {
s = args[i].trim();
if (s.equals("-h") || s.equals("--help")) {
printUsage();
return;
}
if (s.equals("-l") || s.equals("--logfile")) {
logFile = args[++i];
logFlag = true;
Debug.p("Log file set to: " + logFile);
continue;
}
if (s.equals("-p") || s.equals("--port")) {
srvPort = Integer.parseInt(args[++i]);
Debug.p("Service port set to: " + srvPort);
continue;
}
if (s.equals("-b") || s.equals("--bind")) {
bindAddress = args[++i];
Debug.p("Bind address set to: " + bindAddress);
continue;
}
if (s.equals("-g") || s.equals("--backlog")) {
backlog = Integer.parseInt(args[++i]);
Debug.p("Backlog set to: " + backlog);
continue;
}
if (s.equals("-m") || s.equals("--maxthreads")) {
maxThreads = Integer.parseInt(args[++i]);
Debug.p("Max threads set to: " + maxThreads);
continue;
}
if (s.equals("-i") || s.equals("--interval")) {
backupInterval = Integer.parseInt(args[++i]);
Debug.p("Backup interval set to: " + backupInterval);
continue;
}
if (s.equals("-a") || s.equals("--logging")) {
Debug.p("Access logging enabled.");
logFlag = true;
continue;
}
if (s.equals("-c") || s.equals("--config")) {
String dir = args[++i];
Debug.p("Config directory set to: " + dir);
setConfigDir(dir);
continue;
}
System.out.println("Unrecognized option : " + args[i]);
printUsage();
return;
}
// Start the server
startServing();
}
private static void printUsage() {
System.out.println("Usage: java org.apache.vinci.transport.vns.service.VNS [options]");
System.out.println(" -p N --port N");
System.out.println(" Serve on port number N [default=" + srvPort + "]");
System.out.println(" -b ip_address --bind ip_address");
System.out
.println(" Force socket server to bind only to ip_address [default is bind to all]");
System.out.println(" -c dirname --config dirname");
System.out.println(" Look for & write config files in this directory [default=.]");
System.out.println(" -i N --interval N");
System.out.println(" Backup every N seconds [default=" + backupInterval + "]");
System.out.println(" -l fname --logfile fname");
System.out.println(" Access log pathname [default=<configdir>/vns.log]");
System.out.println(" -a --logging");
System.out.println(" Enable access logging (implied by -l/--logfile option)");
System.out.println(" -g N --backlog N");
System.out.println(" Set the ServerSocket backlog [default=" + backlog + "]");
System.out.println(" -m N --maxthreads N");
System.out.println(" Set the maximum size of the VNS thread pool [default=" + maxThreads
+ "].");
System.out.println(" -h --help");
System.out.println(" This help message");
System.exit(0);
}
/* Main run method to initialize the server and start running it */
public static void startServing() throws IOException {
VNS vns;
if (logFlag) {
if (logFile == null) {
logFile = configDir + File.separatorChar + "vns.log";
}
vns = new VNS(srvPort, logFile);
} else {
vns = new VNS(srvPort);
}
// Load the settings files
if (!vns.loadConfig(configFile)) {
System.err.println("failed to load config file: " + configFile);
System.err.println("Check its path and validity. If necessary, restore from backup.");
System.exit(1);
}
vns.loadWorkspaces(workspacesFile);
Debug.p("VNS Workspace : " + vns.WS.workspace);
vns.loadCounters(counterFile);
// Configure and start the backup thread
Debug.p("Starting backup thread, using files " + backupFile + " and " + configFile);
vns.backupThreadRunnable = new BackupThread(vns, backupFile, configFile, backupInterval,
counterFile);
vns.backupThread = new Thread(vns.backupThreadRunnable);
vns.backupThread.start();
// Start accepting connections and processing stuff
Debug.p("Serving on port : " + vns.port);
BaseServer server = null;
if (bindAddress != null) {
server = new BaseServer(vns) {
protected ServerSocket createServerSocket(int port) throws IOException {
return new ServerSocket(port, backlog, InetAddress.getByName(bindAddress));
}
};
} else {
server = new BaseServer(vns) {
protected ServerSocket createServerSocket(int port) throws IOException {
return new ServerSocket(port, backlog);
}
};
}
server.setSocketTimeout(5000);
exitThread exitT = new exitThread(server);
exitT.start();
try {
server.setThreadPoolSize(5, maxThreads);
server.serve(vns.port);
} catch (Throwable e) {
Debug.reportException(e);
} finally {
Debug.p("Exiting.");
System.exit(0);
}
}
/* Constructors */
public VNS() {
SR = new ServiceRegistry();
WS = new WorkspaceConfig(this);
hits = new Hashtable();
totalhits = 0;
starttime = (new Date()).toString();
}
public VNS(int port) {
this();
this.port = port;
}
public VNS(int port, String logFile) throws IOException {
this();
this.port = port;
this.myLogFile = new String(logFile);
this.log = new BufferedWriter(new FileWriter(myLogFile, true));
}
/* Methods to load data from various files */
public boolean loadConfig(String cFile) {
File f = new File(cFile);
if (!f.exists()) {
Debug.p("WARNING: Config file doesn't exist, creating a new empty config file!");
try {
FileWriter writer = new FileWriter(f);
writer.write("<CONFIGURATION></CONFIGURATION>");
writer.close();
} catch (IOException e) {
Debug.reportException(e);
return false;
}
}
Debug.p("Loading config file : " + cFile);
synchronized (SR) {
try {
SR.load(cFile);
return true;
} catch (Exception e) {
Debug.reportException(e);
}
}
return false;
}
public void loadWorkspaces(String wFile) {
Debug.p("Loading workspaces file : " + wFile);
FileReader F = null;
try {
F = new FileReader(wFile);
WS.load(F);
F.close();
} catch (Exception e) {
Debug.reportException(e);
Debug.p("WARNING: failed to load workspace.");
} finally {
try {
F.close();
} catch (Exception e) {
}
}
}
public void loadCounters(String cFile) {
Debug.p("Loading counter file : " + cFile);
FileReader F;
BufferedReader br;
int line = 1;
try {
F = new FileReader(cFile);
} catch (FileNotFoundException e) {
Debug.p("Could not load the counter file : " + cFile);
return;
}
br = new BufferedReader(F);
try {
String s = br.readLine();
if (s == null || s.trim().equals("")) {
Debug.p("Invalid counter file format at line 1: " + cFile);
br.close();
F.close();
return;
}
StringTokenizer str = new StringTokenizer(s);
if (!(str.countTokens() > 1)) {
Debug.p("Invalid counter file format at line 1: " + cFile);
br.close();
F.close();
return;
}
if (!str.nextToken().equals("TOTAL")) {
throw new Exception("First line invalid - does not start with TOTAL");
}
totalhits = Integer.parseInt(str.nextToken());
hits = new Hashtable();
line++;
while ((s = br.readLine()) != null) {
str = new StringTokenizer(s);
hits.put(str.nextToken(), new Integer(str.nextToken()));
line++;
}
br.close();
F.close();
} catch (IOException e2) {
Debug.p("IO Problem while reading from counter file : " + cFile);
Debug.p("Exception was : " + e2);
} catch (NumberFormatException e3) {
Debug.p("Invalid number specified in counter file " + cFile + " at line " + line);
} catch (Exception e4) {
Debug.p("Problem while parsing counter file : " + cFile);
Debug.p("Exception generated : " + e4);
} finally {
try {
br.close();
} catch (IOException e5) {
}
try {
F.close();
} catch (IOException e6) {
}
}
}
/* Method to save data to various files */
public void saveConfig(String cFile) {
Debug.p("Saving to config file : " + cFile);
long startTime = System.currentTimeMillis();
synchronized (SR) {
Writer F = null;
try {
F = new BufferedWriter(new FileWriter(cFile + ".rename"));
try {
SR.save(F);
} finally {
F.close();
}
File real_file = new File(cFile);
real_file.delete();
if (!new File(cFile + ".rename").renameTo(real_file)) {
throw new IOException("FAILED to rename services config file!!!");
}
} catch (IOException e) {
Debug.reportException(e);
Debug.p("Could not save config file : " + cFile);
}
}
startTime = System.currentTimeMillis() - startTime;
Debug.p("Config save required " + startTime + " millis.");
// flush the log too if it needs it...
if (log != null) {
synchronized (log) {
try {
log.flush();
} catch (IOException e) {
Debug.reportException(e);
}
}
}
}
public void saveCounters(String cFile) {
Debug.p("Saving counter file : " + cFile);
FileWriter F;
try {
F = new FileWriter(cFile);
} catch (IOException e) {
Debug.p("Could not save the counter file : " + cFile);
Debug.p("Exception generated : " + e);
return;
}
try {
F.write("TOTAL " + totalhits + "\n");
if (hits == null) {
F.close();
return;
}
Enumeration keys = hits.keys();
Integer value;
String key;
while (keys.hasMoreElements()) {
key = (String) keys.nextElement();
value = (Integer) hits.get(key);
F.write(key.trim() + " " + value + "\n");
}
} catch (IOException e2) {
Debug.reportException(e2);
Debug.p("IO Problem while writing to counter file : " + cFile);
} finally {
try {
if (F != null) {
F.close();
}
} catch (IOException e3) {
Debug.reportException(e3);
}
}
}
public void saveWorkspaces(String wFile) {
Debug.p("Loading workspaces file : " + wFile);
Writer F = null;
try {
F = new BufferedWriter(new FileWriter(wFile));
WS.save(F);
F.close();
} catch (IOException e) {
Debug.p("Could not save workspaces file : " + wFile);
Debug.p("Exception generated : " + e);
} catch (RuntimeException e2) {
Debug.p("Problem while saving workspace file : " + wFile);
Debug.p("Exception generated: " + e2);
} finally {
try {
if (F != null) {
F.close();
}
} catch (IOException e) {
Debug.reportException(e);
}
}
}
/* Main processing routine */
public Transportable eval(Transportable inp) throws ServiceException {
VinciFrame in = (VinciFrame) inp;
VinciFrame out = null;
Debug.p("Request from " + in.fgetString("vinci:REMOTEIP"));
String command;
command = in.fgetString("vinci:COMMAND");
if (command == null) {
out = new VinciFrame();
out.fadd("vinci:ERROR", "Malformed request");
return out;
}
try {
if (VNSConstants.RESOLVE_COMMAND.equals(command)) {
out = resolve(in);
} else if (VNSConstants.SERVEON_COMMAND.equals(command)) {
out = serveon(in);
} else if (command.equals(dirCmdAddService))
out = addService(in);
else if (command.equals(dirCmdAddAlias))
out = addAlias(in);
else if (command.equals(dirCmdUpdateService))
out = updateService(in);
else if (command.equals(dirCmdDelService))
out = delService(in);
else if (command.equals(dirCmdDelAlias))
out = delAlias(in);
else if (command.equals(dirCmdGetList))
out = getList(in);
else if (command.equals(dirCmdGetNames))
out = getNames(in);
else if (command.equals(dirCmdGetHits))
out = getHits(in);
else {
out = new VinciFrame();
out.fadd("vinci:ERROR", "Unrecognized command");
}
} catch (Exception e) {
// Exact translation of the Python code [may not be necessary]
out = new VinciFrame();
out
.fadd(
"vinci:ERROR",
"Critical error :\n"
+ e
+ "\nThe server MAY not respond to further requests.\nPlease notify the administrator\n");
e.printStackTrace();
}
return out;
}
/* Various processing routines */
VinciFrame resolveLocal(VinciFrame in) {
Debug.p("Local resolve");
VinciFrame out = new VinciFrame();
String name = in.fgetString("SERVICE");
if (strip(name) == null)
return new ErrorFrame("No service name specified");
String host = strip(in.fgetString("HOST"));
String realhost = strip(in.fgetString("IP"));
if (strip(realhost) == null && strip(host) != null) {
try {
// Try to resolve the IP if possible
realhost = (InetAddress.getByName(host)).getHostAddress();
} catch (Exception e) {
// realhost = null;
// return error message
return new ErrorFrame("Could not resolve IP address for service " + name);
}
}
String instance = in.fgetString("INSTANCE");
int inst = -1;
try {
inst = Integer.parseInt(instance);
} catch (Exception e) {
inst = -1;
}
String level = in.fgetString("LEVEL");
// Default as specified in SPEC.txt
if (strip(level) == null || level.trim().toLowerCase().equals("none"))
level = "-1";
else if (level.toLowerCase().equals("all")) {
Debug.p("Specific level must be given (not all)");
out.fadd("vinci:ERROR", "Specific level must be given (not all)");
return out;
}
synchronized (SR) {
// Construct a valid number from the level string specified
level = "" + SR.getLevel(name, level);
}
Service[] servicesList;
// Check the cache for matches
CachedItem citem = (CachedItem) checkCache(name + "[" + level + "]");
if (citem == null) {
synchronized (SR) {
// Find all services that match the specified name and are <= level specified
// Also, resolve all aliases to actual services
servicesList = SR.getServices(name, level, true);
}
// Cache the result (if any matches found)
if (servicesList != null && servicesList.length > 0) {
// Cache the resolve result under the actual name and level returned
cache(name + "[" + servicesList[0].level + "]", new CachedItem(servicesList));
// Have a proxy pointer to the entry created if it is not the same
if (!servicesList[0].level.equals(level)) {
cache(name + "[" + level + "]", new ProxyCachedItem(name + "[" + servicesList[0].level
+ "]"));
}
}
} else
servicesList = citem.servicesList;
if (servicesList == null) {
Debug.p("Service " + name + " not found");
out.fadd("vinci:ERROR", "Service " + name + " not found");
return out;
}
Debug.p("Number of services found with name = " + name + ", and level = " + level + " : "
+ servicesList.length);
if (servicesList.length == 0)
System.err.println("NO SERVICES FOUND WITH REALHOST = " + realhost + " name = " + name);
ArrayList v = new ArrayList();
// Filter the services with matching realhost and instance
for (int i = 0; i < servicesList.length; i++) {
if (realhost != null && !servicesList[i].realhost.equals(realhost))
continue;
if (inst > -1 && inst != servicesList[i].instance)
continue;
v.add(servicesList[i]);
}
servicesList = new Service[v.size()];
for (int i = 0; i < v.size(); i++)
servicesList[i] = (Service) v.get(i);
if (servicesList == null || servicesList.length == 0) {
Debug.p("Service " + name + " not found");
out.fadd("vinci:ERROR", "Service " + name + " not found");
return out;
}
VinciFrame temp;
for (int j = 0; j < servicesList.length; j++) {
temp = new VinciFrame();
temp.fadd("HOST", (String) servicesList[j].getAttr("host"));
temp.fadd("PORT", "" + servicesList[j].port);
temp.fadd("INSTANCE", "" + servicesList[j].instance);
if (servicesList[j].meta) {
Object o = servicesList[j].dict.get("META");
if (o instanceof String)
temp.fadd("META", (String) o);
if (o instanceof Frame)
temp.fadd("META", (Frame) o);
}
out.fadd("SERVER", temp);
}
out.fadd("LEVEL", servicesList[0].level); // should all be the same
return out;
}
VinciFrame resolveProxy(VinciFrame in, String workspace) {
Debug.p("Proxy resolve");
if (workspace == null)
workspace = in.fgetString("WORKSPACE");
if (workspace == null || workspace.equals(WS.workspace)) {
// New spec - assume it is local
return resolveLocal(in);
}
Service[] services = SR.getServices(ENV_PROXY);
VinciFrame out = new VinciFrame();
if (services == null || services.length == 0) {
out.fadd("vinci:ERROR", "Cannot locate proxy service");
return out;
}
// Iterate through all the registered proxy services and try to resolve at each
for (int i = 0; i < services.length; i++) {
Service S = services[i];
if (S.realhost == null) {
continue; // Can't do anything if realhost isn't specified
}
try {
// If the host specified is the local one, then use resolveLocal()
// else it will have an infinite loop
if (S.port == srvPort && InetAddress.getLocalHost().getHostAddress().equals(S.realhost))
out = resolveLocal(in);
else {
Debug.p("Resolving with VNS: " + S.realhost + ":" + S.port);
out = BaseClient.rpc(in, S.realhost, S.port);
}
System.out.println(out.toXML());
if (out == null || out instanceof ErrorFrame || strip(out.fgetString("LEVEL")) == null
|| out.fgetString("vinci:ERROR") != null) {
continue; // Check if resolve actually worked
}
return out;
} catch (ServiceException se) {
// Don't need to worry - caused due to an ErrorFrame as the server was unable to
// resolve
System.out.println(se);
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
out.fadd("vinci:ERROR", "Proxy forwarding failed due to " + e);
return out;
}
}
return new ErrorFrame("Proxy forwarding failed");
}
VinciFrame resolveDefaults(VinciFrame in) {
String name = in.fgetString("SERVICE");
String workspace;
VinciFrame out;
if (WS.workspace == null || WS.search.size() == 0)
return resolveLocal(in); // Use resolveLocal() if WS was not defined
for (int i = 0; i < WS.search.size(); i++) {
workspace = ((String) WS.search.get(i));
if (workspace.equals(WS.workspace)) {
Debug.p("Resolving on local workspace ...");
out = resolveLocal(in);
} else {
Debug.p("Resolving on workspace " + workspace + " ...");
out = resolveProxy(in, workspace);
}
if (out.fgetString("vinci:ERROR") == null)
return out;
}
Debug.p("Resolution failed");
return new ErrorFrame("Could not find service " + name + " in default workspaces");
}
VinciFrame resolve(VinciFrame in) {
logRequest(VNSConstants.RESOLVE_COMMAND, in.fgetString("vinci:REMOTEIP"), in
.fgetString("SERVICE"));
if (in.fgetString("WORKSPACE") == null) {
return resolveDefaults(in);
} else {
Debug.p("Resolving on workspace " + in.fgetString("WORKSPACE") + " ...");
return resolveProxy(in, null);
}
}
VinciFrame serveon(VinciFrame in) {
logRequest(VNSConstants.SERVEON_COMMAND, in.fgetString("vinci:REMOTEIP"), null);
Service S = null, srv = null;
VinciFrame out = new VinciFrame();
String name = in.fgetString("SERVICE");
if (strip(name) == null)
return new ErrorFrame("Invalid service name specified : " + name);
String host = in.fgetString("HOST");
if (strip(host) == null) {
Debug.p("Getting host from socket peer info");
host = in.fgetString("vinci:REMOTEHOST");
if (host == null)
host = in.fgetString("vinci:REMOTEIP");
if (host == null)
return new ErrorFrame("Host could not be parsed - specify HOST");
Debug.p("Peer host is : " + host);
}
String level = in.fgetString("LEVEL");
// Default as specified in SPEC.txt
if (strip(level) == null || level.trim().toLowerCase().equals("none"))
level = "0";
if (level.equals("all")) {
out.fadd("vinci:ERROR", "Specific level must be given or none at all");
return out;
}
String instance = in.fgetString("INSTANCE");
try {
instance = "" + Integer.parseInt(instance);
} catch (Exception e) {
instance = null;
}
if (instance == null)
instance = "0";
Debug.p("Host = " + host);
String realhost = in.fgetString("IP");
try {
if (realhost == null)
realhost = InetAddress.getByName(host).getHostAddress();
} catch (Exception e) {
out.fadd("vinci:ERROR", "Could not resolve IP due to Exception " + e);
return out;
}
Debug.p("search: realhost = " + realhost + " - instance = " + instance);
synchronized (SR) {
level = "" + SR.getLevel(name, level);
}
Debug.p("Level = " + level);
// Update the cache
updateCache(name + "[" + level + "]");
Object[] services;
synchronized (SR) {
services = SR.getServices(name, level);
}
if (services != null) {
for (int i = 0; i < services.length; i++) {
S = (Service) services[i];
Debug.p("current: realhost = " + S.realhost + " - instance = " + S.instance);
if (S.realhost.equals(realhost) && instance.equals("" + S.instance)
&& S.level.equals(level)) {
srv = S;
break;
}
}
}
if (srv == null) {
Debug.p("Creating now");
Hashtable H = new Hashtable();
H.put("NAME", name);
H.put("HOST", host);
H.put("LEVEL", level);
H.put("INSTANCE", instance);
H.put("IP", realhost);
srv = new Service(H);
boolean ok = false;
synchronized (SR) {
Debug.p("Adding service : " + H.get("NAME") + ", lvl=" + H.get("LEVEL") + ",instance="
+ H.get("INSTANCE") + ",ip=" + H.get("IP"));
ok = SR.addService(srv);
}
if (!ok) {
out.fadd("vinci:ERROR", "COuld not find or add service " + name);
return out;
}
} else {
if (srv.minport != srv.maxport) { // if it isn't running on a fixed port
final String s_host = srv.host;
final int s_port = srv.port;
new Thread(new Runnable() {
public void run() {
Debug.p("Trying to shutdown old service ...");
VinciFrame shutdown = new VinciFrame();
shutdown
.fadd(
"vinci:SHUTDOWN",
"Identical service started on this host. Use the INSTANCE tag to run multiple instances of the same service on a single host.");
try {
VinciFrame f = BaseClient.rpc(shutdown, s_host, s_port, 10000);
Debug.p("Shutdown response received: " + f.toXML());
} catch (Exception e) {
Debug.p("Old service already closed: " + e);
}
}
}).start();
}
}
srv.updatePort();
out.fadd("HOST", srv.host);
out.fadd("PORT", srv.port);
out.fadd("LEVEL", srv.level);
out.fadd("INSTANCE", srv.instance);
out.fadd("IP", srv.realhost);
return out;
}
VinciFrame addService(VinciFrame in) {
logRequest(dirCmdAddService, in.fgetString("vinci:REMOTEIP"), null);
VinciFrame service = in.fgetVinciFrame("SERVICE");
Hashtable H = new Hashtable();
int total = service.getKeyValuePairCount();
KeyValuePair P;
for (int i = 0; i < total; i++) {
P = service.getKeyValuePair(i);
if (P.isValueALeaf()) {
H.put(P.getKey(), P.getValueAsString());
} else {
H.put(P.getKey(), P.getValue());
}
}
String level = (String) H.get("LEVEL");
if (strip(level) == null) {
H.put("LEVEL", "0"); // default level of 0
}
boolean ok = false;
synchronized (SR) {
Service S;
ok = SR.addService(S = new Service(H));
updateCache(S);
}
return getFrame(ok, "Add Service request failed");
}
VinciFrame addAlias(VinciFrame in) {
logRequest(dirCmdAddAlias, in.fgetString("vinci:REMOTEIP"), null);
VinciFrame service = in.fgetVinciFrame("SERVICE");
boolean ok = true;
if (service.fgetString("NAME") == null || service.fgetString("TARGET") == null)
getFrame(false, "Malformed request");
else {
synchronized (SR) {
ok = SR
.addAlias(new ServiceAlias(service.fgetString("NAME"), service.fgetString("TARGET")));
}
}
return getFrame(ok, "Add alias request failed");
}
VinciFrame delService(VinciFrame in) {
logRequest(dirCmdDelService, in.fgetString("vinci:REMOTEIP"), null);
VinciFrame service = in.fgetVinciFrame("SERVICE");
Hashtable H = new Hashtable();
int total = service.getKeyValuePairCount();
KeyValuePair P = null;
for (int i = 0; i < total; i++) {
P = service.getKeyValuePair(i);
if (P.isValueALeaf()) {
H.put(P.getKey(), P.getValueAsString());
} else {
H.put(P.getKey(), P.getValue());
}
}
String level = (String) H.get("LEVEL");
if (strip(level) == null)
H.put("LEVEL", "0"); // default level of 0
boolean ok = false;
synchronized (SR) {
Service S;
ok = SR.delService(S = new Service(H));
updateCache(S);
}
return getFrame(ok, "Delete Service request failed");
}
VinciFrame delAlias(VinciFrame in) {
logRequest(dirCmdDelAlias, in.fgetString("vinci:REMOTEIP"), null);
VinciFrame service = in.fgetVinciFrame("SERVICE");
boolean ok = true;
if (service.fgetString("NAME") == null || service.fgetString("TARGET") == null) {
getFrame(false, "Malformed request");
} else {
synchronized (SR) {
ok = SR.delAlias(service.fgetString("NAME"));
}
}
return getFrame(ok, "Delete alias request failed");
}
VinciFrame updateService(VinciFrame in) {
logRequest(dirCmdUpdateService, in.fgetString("vinci:REMOTEIP"), null);
VinciFrame service = in.fgetVinciFrame("SERVICE");
Hashtable H = new Hashtable();
int total = service.getKeyValuePairCount();
KeyValuePair P;
for (int i = 0; i < total; i++) {
P = service.getKeyValuePair(i);
if (P.isValueALeaf()) {
H.put(P.getKey(), P.getValueAsString());
} else {
H.put(P.getKey(), P.getValue());
}
}
boolean ok = false;
synchronized (SR) {
Service S;
ok = SR.updateService(S = new Service(H));
updateCache(S);
}
return getFrame(ok, "Update Service request failed");
}
VinciFrame getList(VinciFrame in) {
String level = in.fgetString("LEVEL");
// Default as specified in SPEC.txt
if (level == null || level.trim().toLowerCase().equals("none")) {
level = "-1";
}
String prefix = in.fgetString("PREFIX");
if (prefix == null) {
prefix = "";
}
logRequest(dirCmdGetList, in.fgetString("vinci:REMOTEIP"), prefix);
Object[] servicelist;
synchronized (SR) {
Debug.p("Getting list for level : " + level);
servicelist = SR.listServices(prefix, level);
if (servicelist == null)
servicelist = new Object[0]; // For safety
Debug.p("Matches for getList : " + servicelist.length);
}
VinciFrame F = new VinciFrame();
for (int i = 0; i < servicelist.length; i++) {
if (ServiceAlias.isAlias(servicelist))
F.fadd("SERVICE", ((ServiceAlias) servicelist[i]).toFrame());
else
F.fadd("SERVICE", ((Service) servicelist[i]).toFrame());
}
return F;
}
VinciFrame getNames(VinciFrame in) {
String level = in.fgetString("LEVEL");
// Default as specified in SPEC.txt
if (level == null || level.trim().toLowerCase().equals("none"))
level = "-1";
String prefix = in.fgetString("PREFIX");
if (prefix == null)
prefix = "";
logRequest(dirCmdGetNames, in.fgetString("vinci:REMOTEIP"), prefix);
String[] servicelist;
synchronized (SR) {
Debug.p("Getting names list for level : " + level);
servicelist = SR.listNames(prefix, level);
if (servicelist == null)
servicelist = new String[0]; // For safety
Debug.p("Matches for getNames : " + servicelist.length);
}
VinciFrame F = new VinciFrame();
for (int i = 0; i < servicelist.length; i++) {
F.fadd("SERVICE", servicelist[i]);
}
return F;
}
VinciFrame getHits(VinciFrame in) {
String type = in.fgetString("TYPE");
VinciFrame F = new VinciFrame();
VinciFrame temp;
String key;
if (type != null) {
if (type.equals("all")) {
Enumeration keys = hits.keys();
while (keys.hasMoreElements()) {
key = (String) keys.nextElement();
temp = new VinciFrame();
temp.fadd("TYPE", key);
temp.fadd("COUNT", hits.get(key).toString());
F.fadd("HITS", temp);
}
F.fadd("TOTAL", "" + totalhits);
} else {
if (hits.get(type) != null)
F.fadd("HITS", hits.get(type).toString());
else {
F.fadd("vinci:ERROR", "No such type: " + type);
return F;
}
}
} else {
F.fadd("HITS", "" + totalhits);
}
F.fadd("STARTED", "" + starttime);
return F;
}
/* Caching routines */
private void cache(String s, Object o) {
synchronized (cachedResults) {
cachedResults.put(s, o);
}
}
private void updateCache(String s) {
synchronized (cachedResults) {
cachedResults.remove(s);
}
}
private Service updateCache(Service S) {
String name = (String) S.getAttr("name");
String level = "";
synchronized (SR) {
level = "" + SR.getLevel(name, (String) S.getAttr("level"));
}
updateCache(name + "[" + level + "]");
return S;
}
private Object checkCache(String s) {
synchronized (cachedResults) {
Object o = null;
do {
o = cachedResults.get(s);
if (o == null)
return null;
if (o instanceof ProxyCachedItem)
s = ((ProxyCachedItem) o).altKey;
} while (!(o instanceof CachedItem));
return o;
}
}
/* Helper method to return the correct VinciFrame */
VinciFrame getFrame(boolean ok, String err) {
VinciFrame F = null;
if (ok) {
F = new VinciFrame();
F.fadd("STATUS", "OK");
} else
return new ErrorFrame(err);
return F;
}
/* Helper methods */
public static String strip(String s) {
if (s == null || s.trim().equals(""))
return null;
return s;
}
public static String emptyString(String s) {
if (strip(s) == null)
return "";
return s;
}
/* Logging routines */
void logRequest(String type, String ip, String text) {
if (ip == null) {
ip = BaseServerRunnable.getSocket().getInetAddress().getHostAddress();
if (ip == null) {
ip = "UNKNOWN";
}
}
if (text == null)
text = "";
// Update hit counter
synchronized (hits) {
Integer I = (Integer) hits.get(type);
if (I == null)
I = new Integer(0);
I = new Integer(I.intValue() + 1);
hits.put(type, I);
totalhits++;
}
String ts = (new Date()).toString();
String write_me = ip + " - [" + ts + "] " + type + " " + text + "\n";
Debug.p(write_me);
if (log == null)
return;
synchronized (log) {
// Make an entry in the log file
try {
log.write(write_me);
} catch (IOException e) {
Debug.reportException(e);
}
}
}
public void cleanExit() {
Debug.p("Exiting now ...");
quitFile.delete();
try {
backupThreadRunnable.stop = true;
backupThread.interrupt();
} catch (Exception e) {
Debug.reportException(e);
}
backupThreadRunnable.forceWrite();
if (log != null) {
try {
synchronized (log) {
log.close();
}
} catch (IOException e) {
Debug.reportException(e);
}
}
}
}
/* Class for the backup thread */
class BackupThread implements Runnable {
VNS parent;
String backupFile;
String configFile = null;
String counterFile = null;
int interval;
volatile protected boolean stop = false;
public BackupThread(VNS parent, String backupFile, String configFile, int interval,
String counterFile) {
// We create new strings so that we can start another VNS instance without disturbing this one
this.parent = parent;
this.backupFile = new String(backupFile);
if (configFile != null)
this.configFile = new String(configFile);
if (counterFile != null)
this.counterFile = new String(counterFile);
this.interval = interval * 1000; // Convert secs to millisecs
}
public void forceWrite() {
parent.saveConfig(backupFile);
if (configFile != null)
parent.saveConfig(configFile);
if (counterFile != null)
parent.saveCounters(counterFile);
Debug.p("All files written to disk");
}
public void run() {
Debug.p("Backup thread started");
while (true) {
parent.saveConfig(backupFile);
if (configFile != null) {
parent.saveConfig(configFile);
}
if (counterFile != null) {
parent.saveCounters(counterFile);
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
break;
}
if (stop) {
break;
}
}
Debug.p("Backup thread exitted");
}
}
/* Thread to check for quit command */
class exitThread extends Thread {
BaseServer parent = null;
boolean run = false;
public exitThread(BaseServer parent) {
this.parent = parent;
run = true;
}
public void run() {
if (parent == null)
return;
int i = 0;
char[] cmd = { 'q', 'u', 'i', 't' };
System.out.println(">>>>>>>>>>>>> VNS is up and running! <<<<<<<<<<<<<<<<<");
System.out.println(">>>>>>>>>>>>> Type \'quit\' and hit ENTER to terminate VNS <<<<<<<<<<<<<");
while (run) {
i = 0;
try {
if (System.in.available() > 0) {
while (System.in.available() > 0) {
if ((System.in.read()) != cmd[i])
i = 0;
else
i++;
if (i >= cmd.length) {
run = false;
break;
}
}
}
} catch (Exception e) {
}
try {
Thread.sleep(1000);
if (VNS.quitFile.exists()) {
run = false;
}
} catch (InterruptedException e2) {
run = false;
}
}
System.out.println("Got quit command from user");
parent.shutdownServing();
System.out.println("Request handling thread successfully shutdown");
}
}
/* Encapsulating class for cached item */
class CachedItem {
CachedItem(Service[] s) {
servicesList = s;
}
protected Service[] servicesList = null;
}
/* Points to some other cached entry */
class ProxyCachedItem {
protected String altKey;
ProxyCachedItem(String s) {
altKey = s;
}
}