ServiceTable.java revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* ident "%Z%%M% %I% %E% SMI"
*
* Copyright (c) 1999 by Sun Microsystems, Inc.
* All rights reserved.
*
*/
// SCCS Status: @(#)ServiceTable.java 2.7 11/20/97
// ServiceTable.java: Storage of all services.
// Author: James Kempf
// Created On: Fri Oct 10 14:23:25 1997
// Last Modified By: James Kempf
// Last Modified On: Thu Apr 1 10:33:46 1999
// Update Count: 461
//
package com.sun.slp;
import java.util.*;
import java.io.*;
import java.security.*;
import java.net.*;
/**
* The ServiceTable object records all service registrations. Note
* that any exceptions internal to the service table are processed
* and either returned as SrvRply objects or are reported.
*
* @version 2.7 97/11/20
* @author James Kempf
*/
class ServiceTable extends Object {
// Key for SDAAdvert class.
static final String SDAADVERT = "com.sun.slp.SDAAdvert";
private static final String locationMsg = "Service table";
//
// Instance variables.
//
// The service store.
protected ServiceStore store = null;
//
// Class variables.
// System properties.
static protected SLPConfig conf = null;
// Singleton objects for the service tables.
static protected ServiceTable table = null;
// The ager thread.
static protected AgerThread thrAger = null;
// Time to sleep. Adjusted depending on incoming URLs.
private static long sleepyTime = Defaults.lMaxSleepTime;
//
// Creation of singleton.
//
// Protected constructor.
protected ServiceTable() {
if (thrAger != null) {
return;
}
// Create the ager thread.
thrAger = new AgerThread();
// Set the priority low, so other things (like active discovery)
// take priority.
thrAger.setPriority(Thread.MIN_PRIORITY);
thrAger.start();
}
/**
* Return an SA service store.
*
* @return The distinguished table object.
*/
static ServiceTable getServiceTable()
throws ServiceLocationException {
if (conf == null) {
conf = SLPConfig.getSLPConfig();
}
if (table == null) {
table = createServiceTable();
}
return table;
}
/**
* Return a service table object.
*
* @return The service table object.
*/
private static ServiceTable createServiceTable()
throws ServiceLocationException {
ServiceTable table = new ServiceTable();
table.store = ServiceStoreFactory.createServiceStore();
return table;
}
//
// Support for serializated registrations.
//
/**
* If any serialized registrations are pending, then unserialize
* and register.
*/
public void deserializeTable() {
// If there are any serialized registrations, then get
// them and perform registrations.
String serializedURL = conf.getSerializedRegURL();
if (serializedURL != null) {
ServiceStore serStore = getStoreFromURL(serializedURL);
if (serStore != null) {
registerStore(serStore);
}
}
}
/**
* Serialize the table to the URL.
*
* @param URL String giving the URL to which the store should be
* serialized.
*/
void serializeServiceStore(String URL) {
// Open an object output stream for the URL, serialize through
// the factory.
try {
URL url = new URL(URL);
URLConnection urlConn = url.openConnection();
OutputStream os = urlConn.getOutputStream();
BufferedWriter di =
new BufferedWriter(new OutputStreamWriter(os));
// Serialize the store.
ServiceStoreFactory.serialize(di, store);
} catch (MalformedURLException ex) {
conf.writeLog("st_serialized_malform",
new Object[] {URL});
} catch (UnsupportedEncodingException ex) {
conf.writeLog("st_unsupported_encoding",
new Object[] {URL});
} catch (IOException ex) {
conf.writeLog("st_serialized_ioexception",
new Object[] {URL, ex});
} catch (ServiceLocationException ex) {
conf.writeLog("st_serialized_sle",
new Object[] {URL, ex.getMessage()});
}
}
// Read proxy registrations from the URL.
private ServiceStore getStoreFromURL(String serializedURL) {
ServiceStore serStore = null;
// Open an object input stream for the URL, deserialize through
// the factory.
try {
URL url = new URL(serializedURL);
InputStream is = url.openStream();
BufferedReader di = new BufferedReader(new InputStreamReader(is));
// Deserialize the objects.
serStore =
ServiceStoreFactory.deserializeServiceStore(di);
} catch (MalformedURLException ex) {
conf.writeLog("st_serialized_malform",
new Object[] {serializedURL});
} catch (UnsupportedEncodingException ex) {
conf.writeLog("st_unsupported_encoding",
new Object[] {serializedURL});
} catch (IOException ex) {
conf.writeLog("st_serialized_ioexception",
new Object[] {
serializedURL,
ex.getMessage()});
} catch (ServiceLocationException ex) {
conf.writeLog("st_serialized_sle",
new Object[] {
serializedURL,
ex.getMessage()});
}
return serStore;
}
// Walk the table, performing actual registrations on all records.
private void registerStore(ServiceStore serStore) {
// Walk the table.
Enumeration en = serStore.getServiceRecordsByScope(null);
boolean hasURLSig = conf.getHasSecurity();
boolean hasAttrSig = conf.getHasSecurity();
PermSARegTable pregTable = SARequester.getPermSARegTable();
while (en.hasMoreElements()) {
ServiceStore.ServiceRecord rec =
(ServiceStore.ServiceRecord)en.nextElement();
ServiceURL surl = rec.getServiceURL();
Vector scopes = rec.getScopes();
Vector attrs = rec.getAttrList();
Locale locale = rec.getLocale();
Hashtable urlSig = null;
Hashtable attrSig = null;
// Note that we can't use the Advertiser to register here,
// because we may not be listening yet for registrations.
// We need to do this all by hand.
try {
// Create a registration message for refreshing.
CSrvReg creg = new CSrvReg(false,
locale,
surl,
scopes,
attrs,
null,
null);
// We externalize to a server side message if authentication
// is needed. This creates the auth blocks for the scopes.
// Doing this in any other way is alot more complicated,
// although doing it this way seems kludgy.
if (hasURLSig || hasAttrSig) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
creg.getHeader().externalize(baos, false, true);
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
bais.read(); // pop off version and function code...
bais.read();
DataInputStream dis = new DataInputStream(bais);
SLPHeaderV2 hdr = new SLPHeaderV2();
hdr.parseHeader(SrvLocHeader.SrvReg, dis);
SSrvReg sreg = new SSrvReg(hdr, dis);
// Now we've got it, after much effort. Get the auths.
urlSig = sreg.URLSignature;
attrSig = sreg.attrSignature;
}
store.register(surl, attrs, scopes, locale, urlSig, attrSig);
// Now we've got to put the registration into the
// PermSARegTable. Again, we do everything by hand
// because we can't use Advertiser.
if (surl.getIsPermanent()) {
pregTable.reg(surl, creg);
}
// Report registration.
if (conf.regTest()) {
conf.writeLog("st_reg_add",
new Object[] {
locationMsg,
locale,
surl.getServiceType(),
surl,
attrs,
scopes});
}
} catch (ServiceLocationException ex) {
String msg = ex.getMessage();
conf.writeLog("st_serialized_seex",
new Object[] {
new Integer(ex.getErrorCode()),
surl,
(msg == null ? "<no message>":msg)});
} catch (Exception ex) {
String msg = ex.getMessage();
conf.writeLog("st_serialized_seex",
new Object[] {
surl,
(msg == null ? "<no message>":msg)});
}
}
}
//
// Record aging.
//
//
// Run the thread that ages out records.
//
private class AgerThread extends Thread {
public void run() {
setName("SLP Service Table Age-out");
long alarmTime = sleepyTime; // when to wake up next
long wentToSleep = 0; // what time we went to bed
while (true) {
try {
// Record when we went to sleep.
wentToSleep = System.currentTimeMillis();
// Sleep for the minimum amount of time needed before we
// must wake up and check.
sleep(alarmTime);
} catch (InterruptedException ie) {
// A new registration came in. Calculate how much time
// remains until we would have woken up. If this is
// less than the new sleepyTime, then we set the alarm
// for this time. If it is more, then we set the alarm
// for the new sleepyTime.
long remainingSleepTime =
(wentToSleep + alarmTime) - System.currentTimeMillis();
remainingSleepTime = // just in case...
((remainingSleepTime <= 0) ? 0 : remainingSleepTime);
alarmTime = sleepyTime;
if (remainingSleepTime < alarmTime) {
alarmTime = remainingSleepTime;
}
continue; // we don't have to walk yet...
}
// Walk the table, get the new alarm and sleepy times.
if (table != null) {
table.ageStore();
alarmTime = sleepyTime;
}
}
}
}
/**
* Age the service store.
*/
// this method cannot be private... due to compiler weakness
void ageStore() {
try {
// We synchronize in case somebody registers and tries to
// change sleepy time.
synchronized (store) {
Vector deleted = new Vector();
sleepyTime = store.ageOut(deleted);
// Track unregistered services.
int i, n = deleted.size();
for (i = 0; i < n; i++) {
ServiceStore.ServiceRecord rec =
(ServiceStore.ServiceRecord)deleted.elementAt(i);
ServiceURL surl = rec.getServiceURL();
trackRegisteredServiceTypes(); // it's deleted...
}
}
} catch (RuntimeException ex) {
reportNonfatalException(ex, new Vector(), store);
} catch (ServiceLocationException ex) {
reportNonfatalException(ex, new Vector(), store);
}
}
//
// SLP Service Table operations (register, deregister, etc.)
//
/**
* Process the registration and record if no errors found.
*
* @param req Service registration request message.
* @return SrvLocMsg A service registration acknowledgement.
*/
SrvLocMsg register(SSrvReg req) {
SrvLocHeader hdr = req.getHeader();
Locale locale = hdr.locale;
boolean fresh = hdr.fresh;
Vector scopes = hdr.scopes;
ServiceURL surl = req.URL;
String serviceType = req.serviceType;
Vector attrList = req.attrList;
Hashtable urlSig = req.URLSignature;
Hashtable attrSig = req.attrSignature;
short errorCode =
(fresh ? ServiceLocationException.INVALID_REGISTRATION :
ServiceLocationException.INVALID_UPDATE);
try {
// If a sig block came in, verify it.
if (urlSig != null) {
AuthBlock.verifyAll(urlSig);
}
if (attrSig != null) {
AuthBlock.verifyAll(attrSig);
}
// Check whether the URL has a zero lifetime. If so, it
// isn't cached.
if (surl.getLifetime() <= 0) {
throw
new ServiceLocationException(errorCode,
"st_zero",
new Object[0]);
}
// Check if the service type is restricted. If so, nobody outside
// this process is allowed to register it.
checkForRestrictedType(surl.getServiceType());
// Check that attribute signature bit on implies URL signature
// bit on.
if (attrSig != null && urlSig == null) {
throw
new ServiceLocationException(errorCode,
"st_attr_sig",
new Object[0]);
}
// If a signature and the fresh bit was not set, error since signed
// registrations don't allow updating.
if (urlSig != null && !fresh) {
throw
new ServiceLocationException(
ServiceLocationException.INVALID_UPDATE,
"st_prot_update",
new Object[0]);
}
// Check if scopes are supported.
if (!areSupportedScopes(scopes)) {
throw
new ServiceLocationException(
ServiceLocationException.SCOPE_NOT_SUPPORTED,
"st_scope_unsup",
new Object[0]);
}
// Check if the reg is signed and auth is off or vice versa.
// Check is really simple. If security is on, then all regs
// to this DA/SA server must be signed, so toss out any regs
// that aren't, and vice versa.
if (conf.getHasSecurity() && (urlSig == null || attrSig == null)) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"st_unprot_non_reg",
new Object[0]);
} else if (!conf.getHasSecurity() &&
(urlSig != null || attrSig != null)) {
throw
new ServiceLocationException(
ServiceLocationException.INVALID_REGISTRATION,
"st_prot_non_reg",
new Object[0]);
}
// Merge any duplicates.
Vector attrs = new Vector();
Hashtable attrHash = new Hashtable();
int i, n = attrList.size();
for (i = 0; i < n; i++) {
ServiceLocationAttribute attr =
(ServiceLocationAttribute)attrList.elementAt(i);
ServiceLocationAttribute.mergeDuplicateAttributes(
attr,
attrHash,
attrs,
false);
}
// Store register or update.
boolean existing = false;
if (fresh) {
existing = store.register(surl,
attrs,
scopes,
locale,
urlSig,
attrSig);
// Track registred service types in case we get a
// SAAdvert solicatation.
trackRegisteredServiceTypes();
} else {
store.updateRegistration(surl, attrs, scopes, locale);
}
// Create the reply.
SrvLocMsg ack = req.makeReply(existing);
if (conf.regTest()) {
conf.writeLog((fresh ? "st_reg_add":"st_reg_update"),
new Object[] {
locationMsg,
locale,
serviceType,
surl,
attrs,
scopes});
}
if (conf.traceAll()) {
conf.writeLog("st_dump", new Object[] {locationMsg});
store.dumpServiceStore();
}
// Calculate time increment until next update. This is used
// to adjust the sleep interval in the ager thread.
long sTime = getSleepIncrement(surl);
// We synchronize in case the ager thread is in the middle
// of trying to set the time.
synchronized (store) {
// If we need to wake up sooner, adjust the sleep time.
if (sTime < sleepyTime) {
sleepyTime = sTime;
// Interrupt the thread so we go back to
// sleep for the right amount of time.
thrAger.interrupt();
}
}
return ack;
} catch (ServiceLocationException ex) {
if (conf.traceDrop()) {
conf.writeLog("st_reg_drop",
new Object[] {
locationMsg,
ex.getMessage()+"("+ex.getErrorCode()+")",
locale,
serviceType,
surl,
attrList,
scopes});
}
return hdr.makeErrorReply(ex);
} catch (RuntimeException ex) {
// These exceptions are not declared in throws but can occur
// anywhere.
Vector args = new Vector();
args.addElement(req);
reportNonfatalException(ex, args, store);
return hdr.makeErrorReply(ex);
}
}
/**
* Process the deregistration and return the result in a reply.
*
* @param req Service deregistration request message.
* @return SrvLocMsg A service registration acknowledgement.
*/
SrvLocMsg deregister(SSrvDereg req) {
// We need to determine whether this is an attribute deregistration
// or a deregistration of the entire URL.
SrvLocHeader hdr = req.getHeader();
Locale locale = hdr.locale;
Vector scopes = hdr.scopes;
ServiceURL surl = req.URL;
Hashtable urlSig = req.URLSignature;
Vector tags = req.tags;
short errorCode = ServiceLocationException.OK;
try {
// Verify if signature is nonnull.
if (urlSig != null) {
AuthBlock.verifyAll(urlSig);
}
// Check if the service type is restricted. If so, nobody outside
// this process is allowed to register it.
checkForRestrictedType(surl.getServiceType());
// Error if there's a signature and attempt at deleting attributes.
if ((urlSig != null) && (tags != null)) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"st_prot_attr_dereg",
new Object[0]);
}
// Check if scope is protected and auth is off or vice versa.
// Check is really simple. If security is on, then all scopes
// in this DA/SA server are protected, so toss out any regs
// that aren't, and vice versa.
if (conf.getHasSecurity() && urlSig == null) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"st_unprot_non_dereg",
new Object[0]);
} else if (!conf.getHasSecurity() && urlSig != null) {
throw
new ServiceLocationException(
ServiceLocationException.INVALID_REGISTRATION,
"st_prot_non_dereg",
new Object[0]);
}
// If it's a service URL, then deregister the URL.
if (tags == null) {
store.deregister(surl, scopes, urlSig);
// Track registred service types in case we get a
// SAAdvert solicatation.
trackRegisteredServiceTypes();
} else {
// Just delete the attributes.
store.deleteAttributes(surl, scopes, tags, locale);
}
// Create the reply.
SrvLocMsg ack = req.makeReply();
if (conf.regTest()) {
conf.writeLog((tags == null ? "st_dereg":"st_delattr"),
new Object[] {
locationMsg,
locale,
surl.getServiceType(),
surl,
tags});
}
if (conf.traceAll()) {
conf.writeLog("st_dump",
new Object[] {locationMsg});
store.dumpServiceStore();
}
return ack;
} catch (ServiceLocationException ex) {
if (conf.traceDrop()) {
conf.writeLog((tags == null ?
"st_dereg_drop" : "st_dereg_attr_drop"),
new Object[] {
locationMsg,
ex.getMessage()+"("+ex.getErrorCode()+")",
locale,
surl.getServiceType(),
surl,
tags});
}
return hdr.makeErrorReply(ex);
} catch (RuntimeException ex) {
// These exceptions are not declared in throws but can occur
// anywhere.
Vector args = new Vector();
args.addElement(req);
reportNonfatalException(ex, args, store);
return hdr.makeErrorReply(ex);
}
}
/**
* Process the service type request and return the result in a reply.
*
* @param req Service type request message.
* @return SrvTypeRply A service type reply.
*/
SrvLocMsg findServiceTypes(SSrvTypeMsg req) {
SrvLocHeader hdr = req.getHeader();
Vector scopes = hdr.scopes;
String namingAuthority = req.namingAuthority;
short errorCode = ServiceLocationException.OK;
try {
// Check whether the scope is supported.
if (!areSupportedScopes(scopes)) {
throw
new ServiceLocationException(
ServiceLocationException.SCOPE_NOT_SUPPORTED,
"st_scope_unsup",
new Object[0]);
}
// Get the vector of service types in the store, independent
// of language.
Vector types = store.findServiceTypes(namingAuthority, scopes);
// Create the reply.
SrvLocMsg ack = req.makeReply(types);
if (conf.traceAll()) {
conf.writeLog("st_stypes",
new Object[] {
locationMsg,
namingAuthority,
scopes,
types});
}
return ack;
} catch (ServiceLocationException ex) {
if (conf.traceDrop()) {
conf.writeLog("st_stypes_drop",
new Object[] {
locationMsg,
ex.getMessage()+"("+ex.getErrorCode()+")",
namingAuthority,
scopes,
hdr.locale});
}
return hdr.makeErrorReply(ex);
} catch (RuntimeException ex) {
// These exceptions are not declared in throws but can occur
// anywhere.
Vector args = new Vector();
args.addElement(req);
reportNonfatalException(ex, args, store);
return hdr.makeErrorReply(ex);
}
}
/**
* Process the service request and return the result in a reply.
*
* @param req Service request message.
* @return SrvRply A service reply.
*/
SrvLocMsg findServices(SSrvMsg req) {
SrvLocHeader hdr = req.getHeader();
Locale locale = hdr.locale;
Vector scopes = hdr.scopes;
String serviceType = req.serviceType;
String query = req.query;
short errorCode = ServiceLocationException.OK;
try {
// Check whether the scope is supported.
if (!areSupportedScopes(scopes)) {
throw
new ServiceLocationException(
ServiceLocationException.SCOPE_NOT_SUPPORTED,
"st_scope_unsup",
new Object[0]);
}
// Get the hashtable of returns.
Hashtable returns =
store.findServices(serviceType,
scopes,
query,
locale);
// Get the hashtable of services v.s. scopes, and signatures, if
// any.
Hashtable services =
(Hashtable)returns.get(ServiceStore.FS_SERVICES);
Hashtable signatures =
(Hashtable)returns.get(ServiceStore.FS_SIGTABLE);
boolean hasSignatures = (signatures != null);
// for each candidate URL, make sure it has the requested SPI
// (if any)
if (hasSignatures && !req.spi.equals("")) {
Enumeration allSurls = services.keys();
while (allSurls.hasMoreElements()) {
Object aSurl = allSurls.nextElement();
Hashtable auths = (Hashtable) signatures.get(aSurl);
AuthBlock auth =
AuthBlock.getEquivalentAuth(req.spi, auths);
if (auth == null) {
// doesn't have the requested SPI
services.remove(aSurl);
}
}
}
// Create return message.
SrvLocMsg ack = req.makeReply(services, signatures);
if (conf.traceAll()) {
conf.writeLog("st_sreq",
new Object[] {
locationMsg,
serviceType,
scopes,
query,
locale,
services,
signatures});
}
return ack;
} catch (ServiceLocationException ex) {
if (conf.traceDrop()) {
conf.writeLog("st_sreq_drop",
new Object[] {
locationMsg,
ex.getMessage()+"("+ex.getErrorCode()+")",
serviceType,
scopes,
query,
locale});
}
return hdr.makeErrorReply(ex);
} catch (RuntimeException ex) {
// These exceptions are not declared in throws but can occur
// anywhere.
Vector args = new Vector();
args.addElement(req);
reportNonfatalException(ex, args, store);
return hdr.makeErrorReply(ex);
}
}
/**
* Process the attribute request and return the result in a reply.
*
* @param req Attribute request message.
* @return AttrRply An attribute reply.
*/
SrvLocMsg findAttributes(SAttrMsg req) {
// We need to determine whether this is a request for attributes
// on a specific URL or for an entire service type.
SrvLocHeader hdr = req.getHeader();
Vector scopes = hdr.scopes;
Locale locale = hdr.locale;
ServiceURL surl = req.URL;
String serviceType = req.serviceType;
Vector tags = req.tags;
short errorCode = ServiceLocationException.OK;
try {
// Check whether the scope is supported.
if (!areSupportedScopes(scopes)) {
throw
new ServiceLocationException(
ServiceLocationException.SCOPE_NOT_SUPPORTED,
"st_scope_unsup",
new Object[0]);
}
Vector attributes = null;
Hashtable sig = null;
// If it's a service URL, then get the attributes just for
// that URL.
if (serviceType == null) {
// If the attrs are signed, then error if any tags, since
// we must ask for *all* attributes in for a signed reg
if (!req.spi.equals("") && tags.size() > 0) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"st_par_attr",
new Object[0]);
}
Hashtable ht =
store.findAttributes(surl, scopes, tags, locale);
// Get the attributes and signatures.
attributes = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES);
sig = (Hashtable)ht.get(ServiceStore.FA_SIG);
// make sure the attr has the requested SPI (if any)
if (sig != null && !req.spi.equals("")) {
AuthBlock auth = AuthBlock.getEquivalentAuth(req.spi, sig);
if (auth == null) {
// return empty
attributes = new Vector();
}
}
} else {
if (!req.spi.equals("")) {
throw
new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_FAILED,
"st_par_attr",
new Object[0]);
}
// Otherwise find the attributes for all service types.
attributes =
store.findAttributes(serviceType, scopes, tags, locale);
}
ServiceType type =
(serviceType == null ? surl.getServiceType():
new ServiceType(serviceType));
// Create the reply.
SrvLocMsg ack = req.makeReply(attributes, sig);
if (conf.traceAll()) {
conf.writeLog((serviceType != null ?
"st_st_attr" : "st_url_attr"),
new Object[] {
locationMsg,
(serviceType != null ? serviceType.toString() :
surl.toString()),
scopes,
tags,
locale,
attributes});
}
return ack;
} catch (ServiceLocationException ex) {
if (conf.traceDrop()) {
conf.writeLog((serviceType != null ? "st_st_attr_drop":
"st_url_attr_drop"),
new Object[] {
locationMsg,
ex.getMessage()+"("+ex.getErrorCode()+")",
(serviceType != null ? serviceType.toString() :
surl.toString()),
scopes,
tags,
locale});
}
return hdr.makeErrorReply(ex);
} catch (RuntimeException ex) {
// These exceptions are not declared in throws but can occur
// anywhere.
Vector args = new Vector();
args.addElement(req);
reportNonfatalException(ex, args, store);
return hdr.makeErrorReply(ex);
}
}
// Return the service record corresponding to the URL.
ServiceStore.ServiceRecord getServiceRecord(ServiceURL URL,
Locale locale) {
return store.getServiceRecord(URL, locale);
}
//
// Utility methods.
//
//
// Protected/private methods.
//
// Check whether the type is restricted, through an exception if so.
private void checkForRestrictedType(ServiceType type)
throws ServiceLocationException {
if (Defaults.restrictedTypes.contains(type)) {
throw
new ServiceLocationException(
ServiceLocationException.INVALID_REGISTRATION,
"st_restricted_type",
new Object[] {type});
}
}
// Insert a record for type "service-agent" with attributes having
// the types currently supported, if the new URL is not on the
// list of supported types. This allows us to perform queries
// for supported service types.
private void trackRegisteredServiceTypes()
throws ServiceLocationException {
// First find the types.
Vector types = store.findServiceTypes(Defaults.ALL_AUTHORITIES,
conf.getSAConfiguredScopes());
// Get preconfigured attributes.
Vector attrs = conf.getSAAttributes();
// Make an attribute with the service types.
ServiceLocationAttribute attr =
new ServiceLocationAttribute(Defaults.SERVICE_TYPE_ATTR_ID,
types);
attrs.addElement(attr);
// Construct URL to use on all interfaces.
Vector interfaces = conf.getInterfaces();
int i, n = interfaces.size();
for (i = 0; i < n; i++) {
InetAddress addr = (InetAddress)interfaces.elementAt(i);
ServiceURL url =
new ServiceURL(Defaults.SUN_SA_SERVICE_TYPE + "://" +
addr.getHostAddress(),
ServiceURL.LIFETIME_MAXIMUM);
Vector scopes = conf.getSAOnlyScopes();
Locale locale = Defaults.locale;
// Make a new registration for this SA.
store.register(url,
attrs,
scopes,
locale,
null,
null); // we could sign, but we do that later...
}
// Note that we don't need a refresh on the URLs because they
// will get refreshed when the service URLs that they track
// are refreshed. If the tracked URLs aren't refreshed, then
// these will get updated when the tracked URLs age out.
}
// Return true if the scopes in the vector are supported by the DA
// or SA server.
final private boolean areSupportedScopes(Vector scopes) {
Vector configuredScopes = conf.getSAConfiguredScopes();
Vector saOnlyScopes = conf.getSAOnlyScopes();
int i = 0;
while (i < scopes.size()) {
Object o = scopes.elementAt(i);
// Remove it if we don't support it.
if (!configuredScopes.contains(o) && !saOnlyScopes.contains(o)) {
// This will shift the Vector's elements down one, so
// don't increment i
scopes.removeElementAt(i);
} else {
i++;
}
}
if (scopes.size() <= 0) {
return false;
}
return true;
}
/**
* Return the sleep increment from the URL lifetime. Used by the
* ServiceStore to calculate the new sleep interval in addition
* to this class, when a new URL comes in. The algorithm
* subtracts x% of the lifetime from the lifetime and schedules the
* timeout at that time.
*
* @param url The URL to use for calculation.
* @return The sleep interval.
*/
private long getSleepIncrement(ServiceURL url) {
long urlLifetime = (long)(url.getLifetime() * 1000);
long increment =
(long)((float)urlLifetime * Defaults.fRefreshGranularity);
long sTime = urlLifetime - increment;
// If URL lives only one second, update every half second.
if (sTime <= 0) {
sTime = 500;
}
return sTime;
}
// Make a DAADvert for the DA service request. This only applies
// to DAs, not to SA servers.
SrvLocMsg
makeDAAdvert(SSrvMsg rqst,
InetAddress daAddr,
SLPConfig conf) {
SrvLocHeader hdr = rqst.getHeader();
Vector scopes = hdr.scopes;
short xid = hdr.xid;
String query = rqst.query;
try {
// If security is on, proceed only if we can sign as rqst.spi
if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_UNKNOWN,
"st_cant_sign_as",
new Object[] {rqst.spi});
}
// Get the hashtable of service URLs v.s. scopes.
Hashtable services =
ServerDATable.getServerDATable().returnMatchingDAs(query);
// Go through the table checking whether the IP address came back.
Enumeration urls = services.keys();
boolean foundIt = false;
String strDAAddr = daAddr.getHostAddress();
while (urls.hasMoreElements()) {
ServiceURL url = (ServiceURL)urls.nextElement();
if (url.getHost().equals(strDAAddr)) {
foundIt = true;
break;
}
}
// If we didn't find anything, make a null service reply.
if (!foundIt) {
return rqst.makeReply(new Hashtable(), new Hashtable());
}
return makeDAAdvert(hdr, daAddr, xid, scopes, conf);
} catch (ServiceLocationException ex) {
return hdr.makeErrorReply(ex);
}
}
// Make a DAAdvert from the input arguments.
SrvLocMsg
makeDAAdvert(SrvLocHeader hdr,
InetAddress daAddr,
short xid,
Vector scopes,
SLPConfig config)
throws ServiceLocationException {
// If this is a request for a V1 Advert, truncate the scopes vector
// since DA solicitations in V1 are always unscoped
if (hdr.version == 1) {
scopes = new Vector();
}
// Check if we support scopes first. If not, return an
// error reply unless the scope vector is zero. Upper layers
// must sort out whether this is a unicast or multicast.
if (scopes.size() > 0 && !areSupportedScopes(scopes)) {
throw
new ServiceLocationException(
ServiceLocationException.SCOPE_NOT_SUPPORTED,
"st_scope_unsup",
new Object[0]);
}
// Get the service store's timestamp. This must be the
// time since last stateless reboot for a stateful store,
// or the current time.
long timestamp = store.getStateTimestamp();
ServiceURL url =
new ServiceURL(Defaults.DA_SERVICE_TYPE + "://" +
daAddr.getHostAddress(),
ServiceURL.LIFETIME_DEFAULT);
SDAAdvert advert =
hdr.getDAAdvert(xid,
timestamp,
url,
scopes,
conf.getDAAttributes());
return advert;
}
// Make a SAADvert for the SA service request. This only applies
// to SA servers, not DA's. Note that we only advertise the "public"
// scopes, not the private ones.
SSAAdvert
makeSAAdvert(SSrvMsg rqst,
InetAddress interfac,
SLPConfig conf)
throws ServiceLocationException {
SrvLocHeader hdr = rqst.getHeader();
int version = hdr.version;
short xid = hdr.xid;
Locale locale = hdr.locale;
Vector scopes = hdr.scopes;
String query = rqst.query;
String serviceType = rqst.serviceType;
Vector saOnlyScopes = conf.getSAOnlyScopes();
// If security is on, proceed only if we can sign as rqst.spi
if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) {
throw new ServiceLocationException(
ServiceLocationException.AUTHENTICATION_UNKNOWN,
"st_cant_sign_as",
new Object[] {rqst.spi});
}
// Check if we support scopes first. Note that this may allow
// someone to get at the SA only scopes off machine, but that's
// OK. Since the SAAdvert is only ever multicast, this is OK.
if (!areSupportedScopes(scopes) && !(scopes.size() <= 0)) {
return null;
}
// If the scopes vector is null, then use all configured scopes.
if (scopes.size() <= 0) {
scopes = (Vector)conf.getSAConfiguredScopes().clone();
}
// Check to be sure the query matches.
// If it doesn't, we don't need to return anything.
Hashtable returns =
store.findServices(Defaults.SUN_SA_SERVICE_TYPE.toString(),
saOnlyScopes,
query,
Defaults.locale);
Hashtable services =
(Hashtable)returns.get(ServiceStore.FS_SERVICES);
Enumeration en = services.keys();
// Indicates we don't support the service type.
if (!en.hasMoreElements()) {
return null;
}
// Find the URL to use. The interface on which the message came in
// needs to match one of the registered URLs.
ServiceURL url = null;
ServiceURL surl = null;
String addr = interfac.getHostAddress();
while (en.hasMoreElements()) {
surl = (ServiceURL)en.nextElement();
if (addr.equals(surl.getHost())) {
url = new ServiceURL(Defaults.SA_SERVICE_TYPE + "://" +
addr,
ServiceURL.LIFETIME_DEFAULT);
break;
}
}
// If none of the URLs matched this interface, then return null.
if (url == null) {
return null;
}
// Find the SA's attributes.
Hashtable ht =
store.findAttributes(surl,
saOnlyScopes,
new Vector(),
Defaults.locale);
Vector attrs = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES);
// Construct return.
return
new SSAAdvert(version,
xid,
locale,
url,
conf.getSAConfiguredScopes(), // report all scopes...
attrs);
}
/**
* Report a fatal exception to the log.
*
* @param ex The exception to report.
*/
protected static void reportFatalException(Exception ex) {
reportException(true, ex, new Vector());
if (table != null) {
table.store.dumpServiceStore();
}
conf.writeLog("exiting_msg", new Object[0]);
System.exit(1);
}
/**
* Report a nonfatal exception to the log.
*
* @param ex The exception to report.
* @param args The method arguments.
* @param store The service store being processed.
*/
protected static void reportNonfatalException(Exception ex,
Vector args,
ServiceStore store) {
reportException(false, ex, args);
if (conf.traceAll()) {
store.dumpServiceStore();
}
}
/**
* Report an exception to the log.
*
* @param isFatal Indicates whether the exception is fatal or not.
* @param ex The exception to report.
* @param args A potentially null vector of arguments to the
* method where the exception was caught.
*/
private static void
reportException(boolean isFatal, Exception ex, Vector args) {
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
// Get the backtrace.
ex.printStackTrace(writer);
String severity = (isFatal ? "fatal_error":"nonfatal_error");
String msg = ex.getMessage();
if (msg == null) {
msg = conf.formatMessage("no_message", new Object[0]);
} else if (ex instanceof ServiceLocationException) {
msg = msg +
"(" + ((ServiceLocationException)ex).getErrorCode() + ")";
}
StringBuffer argMsg = new StringBuffer();
int i, n = args.size();
for (i = 0; i < n; i++) {
argMsg.append("\n (" + Integer.toString(i) + "):" +
args.elementAt(i).toString());
}
conf.writeLog(severity,
new Object[] {
ex.getClass().getName(),
msg,
argMsg,
sw.toString()});
}
}