package com.dbxml.labrador.broker;
/*
* The dbXML Labrador Software License, Version 1.0
*
*
* Copyright (c) 2003 The dbXML Group, L.L.C. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by The
* dbXML Group, L.L.C. (http://www.dbxml.com/)."
* Alternately, this acknowledgment may appear in the software
* itself, if and wherever such third-party acknowledgments normally
* appear.
*
* 4. The names "Labrador" and "dbXML Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* info@dbxml.com
*
* 5. Products derived from this software may not be called "Labrador",
* nor may "Labrador" appear in their name, without prior written
* permission of The dbXML Group, L.L.C..
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE DBXML GROUP, L.L.C. OR ITS
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* $Id: Broker.java,v 1.11 2003/12/15 06:43:10 bradford Exp $
*/
import com.dbxml.labrador.*;
import com.dbxml.labrador.exceptions.DescriberException;
import com.dbxml.labrador.exceptions.FilterException;
import com.dbxml.labrador.exceptions.HandlerException;
import com.dbxml.labrador.exceptions.RequestException;
import com.dbxml.labrador.exceptions.ResolverException;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* Broker is the Labrador Request Broker.
* <br /><br />
* You'll notice that this class is rather light. Actual marshalling
* is handled by Handler implementations and object Instances are
* resolved by Resolvers.
*/
public final class Broker {
private static final String IDF_STR = " [yyyy-MM-dd HH:mm:ss] ";
private static final DateFormat IDF = new SimpleDateFormat(IDF_STR, new DateFormatSymbols(Locale.US));
private static final Resolver[] EmptyResolvers = new Resolver[0];
private static final Handler[] EmptyHandlers = new Handler[0];
private static final Describer[] EmptyDescribers = new Describer[0];
private static final Filter[] EmptyFilters = new Filter[0];
public static final String PRODUCT_NAME = "Labrador";
public static final String PRODUCT_VERSION = "0.7";
public static final int PRODUCT_MAJOR_VERSION = 0;
public static final int PRODUCT_MINOR_VERSION = 7;
public static final String AUTHOR_NAME = "The dbXML Group";
public static final String AUTHOR_URL = "http://www.dbxml.com";
public static final String COPYRIGHT_YEAR = "2003";
public static final String PROP_QUIET = "labrador.quiet";
public static final String PROP_CONFIG = "labrador.config";
public static final String PROP_INSTANCES = "labrador.instances";
public static final String DEFAULT_CONFIG = "labrador.xml";
private static final ThreadLocal context = new ThreadLocal();
private static Broker broker;
private static boolean quiet;
static {
new Broker();
}
private Resolver[] resolvers = EmptyResolvers;
private Object resolverMutex = new Object();
private Handler[] handlers = EmptyHandlers;
private Object handlerMutex = new Object();
private Map describers = new HashMap();
private Object describerMutex = new Object();
private Filter[] filters = EmptyFilters;
private Object filterMutex = new Object();
private Broker() {
broker = this;
String quietString = System.getProperty(PROP_QUIET);
if ( quietString != null )
quiet = "yes|true|1|".indexOf(quietString + "|") != -1;
new Configurator(this).configure();
}
/**
* getInstance returns the Broker instance (Singleton).
*
* @return The Broker instance
*/
public static Broker getInstance() {
return broker;
}
/**
* addDescriber registers a Describer with the Broker so that it can
* be returned using its type at a later time. Only one Describer can
* be registered for any one type. Most Handlers will take it upon
* themselves to register a Describer when they initialize, so this
* method needn't be called directly.
*
* @param describer The Describer to register
*/
public void addDescriber(Describer describer) {
synchronized ( describerMutex ) {
describers.put(describer.getType(), describer);
}
println("Describer: " + describer.getClass().getName());
}
/**
* getDescriber returns a Describer based on the specified type, or
* throws a DescriberException no Describer is mapped to that type.
*
* @param type The Describer type to look up
* @return The Describer
* @throws DescriberException if no Describer is found
*/
public Describer getDescriber(String type) throws DescriberException {
synchronized ( describerMutex ) {
Describer result = (Describer)describers.get(type);
if ( result != null )
return result;
}
throw new DescriberException("No Describer for '"+type+"'");
}
/**
* removeDescriber removes the named Describer from the Broker.
*
* @param type The Describer type to remove
* @return The Describer that was removed
* @throws DescriberException if no Describer is found
*/
public Describer removeDescriber(String type) throws DescriberException {
synchronized ( describerMutex ) {
Describer result = (Describer)describers.remove(type);
if ( result != null )
return result;
}
throw new DescriberException("Could not remove Describer for '"+type+"'");
}
/**
* listDescribers returns the set of Describers that are currently
* registered with the Broker.
*
* @return The set of Describers
*/
public Describer[] listDescribers() {
synchronized ( describerMutex ) {
return (Describer[])describers.values().toArray(EmptyDescribers);
}
}
/**
* addResolver adds a Resolver to the Broker. Resolvers will typically
* add themselves via a static initializer, so this method needn't be
* called directly.
*
* @param resolver The Resolver to add
*/
public void addResolver(Resolver resolver) {
synchronized ( resolverMutex ) {
Resolver[] newResolvers = new Resolver[resolvers.length + 1];
if ( resolvers.length > 0 )
System.arraycopy(resolvers, 0, newResolvers, 0, resolvers.length);
newResolvers[newResolvers.length-1] = resolver;
resolvers = newResolvers;
}
println("Resolver: " + resolver.getClass().getName());
}
/**
* getBestResolver returns the most appropriate Resolver for the
* specified ID. If no Resolver is available for the ID, a
* ResolverException is thrown.
*
* @param id The ID to test
* @return The appropriate Resolver for the ID
* @throws ResolverException if no Resolver is found
*/
public Resolver getBestResolver(ID id) throws ResolverException {
synchronized ( resolverMutex ) {
for ( int i = 0; i < resolvers.length; i++ )
if ( resolvers[i].isIDValid(id) )
return resolvers[i];
}
throw new ResolverException("No Resolver for '"+id+"'");
}
/**
* removeResolver removes the specified Resolver from the Broker.
*
* @param resolver The Resolver to remove
* @return The Resolver that was removed
* @throws ResolverException if not Resolver is found
*/
public Resolver removeResolver(Resolver resolver) throws ResolverException {
synchronized ( resolverMutex ) {
// Check to see if this Resolver is even in the set
for ( int i = 0; i < resolvers.length; i++ )
if ( resolvers[i] == resolver ) {
Resolver[] newResolvers = new Resolver[resolvers.length - 1];
if ( i > 0 )
System.arraycopy(resolvers, 0, newResolvers, 0, i);
if ( i < newResolvers.length )
System.arraycopy(resolvers, i + 1, newResolvers, i, newResolvers.length - i);
resolvers = newResolvers;
return resolver;
}
}
throw new ResolverException("Could not remove Resolver");
}
/**
* listResolvers returns the set of Resolvers that are currently
* registered with the Broker.
*
* @return The set of Resolvers
*/
public Resolver[] listResolvers() {
synchronized ( resolverMutex ) {
return resolvers;
}
}
/**
* addHandler adds a Handler to the Broker. Handlers will typically
* add themselves via a static initializer, so this method needn't be
* called directly.
*
* @param handler The Handler to add
*/
public void addHandler(Handler handler) {
synchronized ( handlerMutex ) {
Handler[] newHandlers = new Handler[handlers.length + 1];
if ( handlers.length > 0 )
System.arraycopy(handlers, 0, newHandlers, 0, handlers.length);
newHandlers[newHandlers.length-1] = handler;
handlers = newHandlers;
}
println("Handler: " + handler.getClass().getName());
}
/**
* getBestHandler returns the most appropriate Handler for the
* specified Request. If no Handler is available for the Request,
* a HandlerException is thrown.
*
* @param request The Request to test
* @return The appropriate Handler for the Request
* @throws HandlerException if no Handler can be resolved
*/
public Handler getBestHandler(Request request) throws HandlerException {
synchronized ( handlerMutex ) {
for ( int i = 0; i < handlers.length; i++ )
if ( handlers[i].isRequestValid(request) )
return handlers[i];
}
throw new HandlerException("No Handler for '"+request.getPath()+"'");
}
/**
* removeHandler removes the specified Handler from the Broker.
*
* @param handler The Handler to remove
* @return The Handler that was removed
* @throws HandlerException if the Handler is not found
*/
public Handler removeHandler(Handler handler) throws HandlerException {
synchronized ( handlerMutex ) {
// Check to see if this Handler is even in the set
for ( int i = 0; i < handlers.length; i++ )
if ( handlers[i] == handler ) {
Handler[] newHandlers = new Handler[handlers.length - 1];
if ( i > 0 )
System.arraycopy(handlers, 0, newHandlers, 0, i);
if ( i < newHandlers.length )
System.arraycopy(handlers, i + 1, newHandlers, i, newHandlers.length - i);
handlers = newHandlers;
return handler;
}
}
throw new HandlerException("Could not remove Handler for '"+handler.getProtocol()+"'");
}
/**
* listHandlers returns the set of Handlers that are currently
* registered with the Broker.
*
* @return The set of Handlers
*/
public Handler[] listHandlers() {
synchronized ( handlerMutex ) {
return handlers;
}
}
/**
* addFilter adds a Filter to the Broker. Filters will typically
* add themselves via a static initializer, so this method needn't be
* called directly.
*
* @param filter The Filter to add
*/
public void addFilter(Filter filter) {
synchronized ( filterMutex ) {
Filter[] newFilters = new Filter[filters.length + 1];
if ( filters.length > 0 )
System.arraycopy(filters, 0, newFilters, 0, filters.length);
newFilters[newFilters.length-1] = filter;
filters = newFilters;
}
println("Filter: " + filter.getClass().getName());
}
/**
* getFilterCount returns the count of registered Filters.
*
* @return The Filter count
*/
public int getFilterCount() {
synchronized ( filterMutex ) {
return filters.length;
}
}
/**
* getFilter returns the Filter at the specified index.
*
* @param index The Filter index to retrieve
* @return The Filter
* @throws FilterException if no Filter is found
*/
public Filter getFilter(int index) throws FilterException {
synchronized ( filterMutex ) {
if ( index >= 0 && index < filters.length )
return filters[index];
}
throw new FilterException("Filter not found at index '"+index+"'");
}
/**
* removeFilter removes the specified Filter from the Broker.
*
* @param filter The Filter to remove
* @return The Filter that was removed
* @throws FilterException if no Filter is found
*/
public Filter removeFilter(Filter filter) throws FilterException {
synchronized ( filterMutex ) {
// Check to see if this Filter is even in the set
for ( int i = 0; i < filters.length; i++ )
if ( filters[i] == filter ) {
Filter[] newFilters = new Filter[filters.length - 1];
if ( i > 0 )
System.arraycopy(filters, 0, newFilters, 0, i);
if ( i < newFilters.length )
System.arraycopy(filters, i + 1, newFilters, i, newFilters.length - i);
filters = newFilters;
return filter;
}
}
throw new FilterException("Could not remove Filter");
}
/**
* listFilters returns the set of Filters that are currently
* registered with the Broker.
*
* @return The set of Filters
*/
public Filter[] listFilters() {
synchronized ( filterMutex ) {
return filters;
}
}
/**
* getBrokerContext returns the BrokerContext variable that is
* associated with the current Thread.
*
* @return The BrokerContext
*/
public BrokerContext getBrokerContext() {
return (BrokerContext)context.get();
}
/**
* processRequest process a request based on the specified Request
* and Response objects.
*
* @param request The Request
* @param response The Response
* @throws RequestException if an error occurs
*/
public void processRequest(Request request, Response response) throws RequestException {
BrokerContextImpl ctx = new BrokerContextImpl(this, request, response);
context.set(ctx);
ctx.process();
}
private static final String stampMessage(char icon, String message) {
StringBuffer sb = new StringBuffer(IDF_STR.length() + message.length() + 1);
sb.append(icon);
sb.append(IDF.format(new Date()));
sb.append(message);
return sb.toString();
}
public static final void println(String message) {
if ( !quiet ) {
String outMsg = stampMessage('+', message);
System.out.println(outMsg);
}
}
public static final void printwarning(String message) {
if ( !quiet ) {
String outMsg = stampMessage('*', message);
System.out.print(outMsg);
}
}
public static final void printerr(String message) {
if ( !quiet ) {
String outMsg = stampMessage('!', message);
System.out.println(outMsg);
}
}
}