/*
* 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 com.sun.jini.outrigger;
import java.util.Map;
import java.io.IOException;
import java.rmi.Remote;
import java.rmi.MarshalledObject;
import java.rmi.RemoteException;
import java.rmi.activation.ActivationID;
import java.rmi.activation.ActivationException;
import javax.security.auth.login.LoginException;
import net.jini.core.discovery.LookupLocator;
import net.jini.core.entry.Entry;
import net.jini.core.event.EventRegistration;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionException;
import net.jini.core.transaction.UnknownTransactionException;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lease.UnknownLeaseException;
import net.jini.export.ProxyAccessor;
import net.jini.space.JavaSpace;
import net.jini.space.InternalSpaceException;
import net.jini.security.TrustVerifier;
import net.jini.security.proxytrust.ServerProxyTrust;
import net.jini.config.ConfigurationException;
import net.jini.id.Uuid;
import com.sun.jini.start.LifeCycle;
/**
* For various reasons there is code that we would like
* to run before every incoming remote call. To accomplish
* this we wrap the server in an object that will run
* the common code and then delegate to the server to
* do the actual work. This is a base class for these
* wrappers.
*
* @author Sun Microsystems, Inc.
* @since 2.0
*/
class OutriggerServerWrapper
implements OutriggerServer, ServerProxyTrust, ProxyAccessor
{
/** The object being delegated to */
private final OutriggerServerImpl delegate;
/**
* If <code>false</code>, hold calls until it becomes
* <code>true</code>
*/
private boolean allowCalls;
/**
* If non-null cause incoming calls to immediately throw this
* exception. Takes presidents over <code>holdCalls</code>. This
* field is only set to an <code>Error</code>,
* <code>RemoteException</code>, or <code>RuntimeException</code>
* and thus can be thrown by an of <code>OutriggerServer</code>'s
* methods.
*/
private Throwable failCallsWith;
/**
* Create an <code>OutriggerServerWrapper</code> that
* will delegate to a non-activatable <code>OutriggerServerImpl</code>
* created with the specified configuration and wrapped by
* <code>this</code>.
* @param configArgs set of strings to be used to obtain a
* <code>Configuration</code>.
* @param lifeCycle the object to notify when this
* service is destroyed.
* @param persistent If <code>true</code> will throw an
* <code>ConfigurationException</code>
* if there is no persistence directory or
* store specified in the configuration.
* @throws IOException if there is problem exporting the server.
* @throws ConfigurationException if the <code>Configuration</code> is
* malformed.
* @throws LoginException if the <code>loginContext</code> specified
* in the configuration is non-null and throws
* an exception when login is attempted.
*/
OutriggerServerWrapper(String[] configArgs, LifeCycle lifeCycle,
boolean persistent)
throws IOException, ConfigurationException, LoginException
{
try {
delegate = new OutriggerServerImpl(null, lifeCycle, configArgs,
persistent, this);
} catch (ActivationException e) {
throw new AssertionError(e);
}
}
/**
* Create an <code>OutriggerServerWrapper</code> that
* will delegate to an <code>OutriggerServerImpl</code>
* created with the specified argument and wrapped by <code>this</code>.
* @param activationID of the server, must not be <code>null</code>.
* @param configArgs set of strings to be used to obtain a
* <code>Configuration</code>.
* @throws IOException if there is problem recovering data
* from disk, exporting the server, or unpacking
* <code>data</code>.
* @throws ConfigurationException if the <code>Configuration</code> is
* malformed.
* @throws ActivationException if activatable and there
* is a problem getting a reference to the activation system.
* @throws LoginException if the <code>loginContext</code> specified
* in the configuration is non-null and throws
* an exception when login is attempted.
* @throws NullPointerException if <code>activationID</code>
* is <code>null</code>.
*/
OutriggerServerWrapper(ActivationID activationID, String[] configArgs)
throws IOException, ConfigurationException, LoginException,
ActivationException
{
if (activationID == null)
throw new NullPointerException("activationID must be non-null");
delegate = new OutriggerServerImpl(activationID, null, configArgs,
true, this);
}
/**
* Cause incoming calls to block until further notice.
*/
synchronized void holdCalls() {
failCallsWith = null;
allowCalls = false;
notifyAll();
}
/**
* Cause in new or blocked calls to fail with
* the specified exception.
* @throws IllegalArgumentException if <code>t</code>
* is not an <code>Error</code>, <code>RemoteException</code>,
* or <code>RuntimeException</code>.
* @throws NullPointerException if <code>t</code> is <code>null</code>.
*/
synchronized void rejectCalls(Throwable t) {
if (t == null)
throw new NullPointerException("Throwable must not be null");
if (!((t instanceof Error) ||
(t instanceof RuntimeException) ||
(t instanceof RemoteException)))
throw new IllegalArgumentException("t must be an exception " +
"that can be thrown from any of OutriggerServer's methods");
failCallsWith = t;
allowCalls = true;
notifyAll();
}
/**
* Allow incoming calls.
*/
synchronized void allowCalls() {
failCallsWith = null;
allowCalls = true;
notifyAll();
}
/**
* Block until calls are allowed, or until calls
* are to be rejected.
* @throws RemoteException If calls are being rejected
* with <code>RemoteException</code>s.
* @throws RuntimeException If calls are being rejected
* with <code>RuntimeException</code>s.
* @throws Error If calls are being rejected
* with <code>Error</code>s.
*/
private synchronized void gate() throws RemoteException {
while (!allowCalls || failCallsWith != null) {
if (failCallsWith != null) {
if (failCallsWith instanceof RemoteException)
throw (RemoteException)failCallsWith;
else if (failCallsWith instanceof Error)
throw (Error)failCallsWith;
else if (failCallsWith instanceof RuntimeException)
throw (RuntimeException)failCallsWith;
else
throw new AssertionError("Wrapper trying to " +
"throw " + failCallsWith);
}
if (!allowCalls) {
try {
wait();
} catch (InterruptedException e) {
throw new
InternalSpaceException("gate method interrupted");
}
}
}
}
public long[] write(EntryRep entry, Transaction txn, long lease)
throws TransactionException, RemoteException
{
gate();
return delegate.write(entry, txn, lease);
}
public Object read(EntryRep tmpl, Transaction txn, long timeout,
QueryCookie cookie)
throws TransactionException, RemoteException, InterruptedException
{
gate();
return delegate.read(tmpl, txn, timeout, cookie);
}
public Object readIfExists(EntryRep tmpl, Transaction txn, long timeout,
QueryCookie cookie)
throws TransactionException, RemoteException, InterruptedException
{
gate();
return delegate.readIfExists(tmpl, txn, timeout, cookie);
}
public Object take(EntryRep tmpl, Transaction txn, long timeout,
QueryCookie cookie)
throws TransactionException, RemoteException, InterruptedException
{
gate();
return delegate.take(tmpl, txn, timeout, cookie);
}
public Object takeIfExists(EntryRep tmpl, Transaction txn, long timeout,
QueryCookie cookie)
throws TransactionException, RemoteException, InterruptedException
{
gate();
return delegate.takeIfExists(tmpl, txn, timeout, cookie);
}
public EventRegistration
notify(EntryRep tmpl, Transaction txn, RemoteEventListener listener,
long lease, MarshalledObject handback)
throws TransactionException, RemoteException
{
gate();
return delegate.notify(tmpl, txn, listener, lease, handback);
}
public EventRegistration registerForAvailabilityEvent(EntryRep[] tmpls,
Transaction txn, boolean visibilityOnly, RemoteEventListener listener,
long leaseTime, MarshalledObject handback)
throws TransactionException, RemoteException
{
gate();
return delegate.registerForAvailabilityEvent(tmpls, txn, visibilityOnly,
listener, leaseTime, handback);
}
public long[] write(EntryRep[] entries, Transaction txn, long[] leaseTimes)
throws TransactionException, RemoteException
{
gate();
return delegate.write(entries, txn, leaseTimes);
}
public Object take(EntryRep[] tmpls, Transaction tr, long timeout,
int limit, QueryCookie cookie)
throws TransactionException, RemoteException
{
gate();
return delegate.take(tmpls, tr, timeout, limit, cookie);
}
public MatchSetData contents(EntryRep[] tmpls, Transaction tr,
long leaseTime, long limit)
throws TransactionException, RemoteException
{
gate();
return delegate.contents(tmpls, tr, leaseTime, limit);
}
public EntryRep[] nextBatch(Uuid contentsQueryUuid, Uuid entryUuid)
throws RemoteException
{
gate();
return delegate.nextBatch(contentsQueryUuid, entryUuid);
}
public long renew(Uuid cookie, long extension)
throws LeaseDeniedException, UnknownLeaseException, RemoteException
{
gate();
return delegate.renew(cookie, extension);
}
public void cancel(Uuid cookie)
throws UnknownLeaseException, RemoteException
{
gate();
delegate.cancel(cookie);
}
public Object getAdmin() throws RemoteException {
gate();
return delegate.getAdmin();
}
public int prepare(TransactionManager mgr, long id)
throws UnknownTransactionException, RemoteException
{
gate();
return delegate.prepare(mgr, id);
}
public void commit(TransactionManager mgr, long id)
throws UnknownTransactionException, RemoteException
{
gate();
delegate.commit(mgr, id);
}
public void abort(TransactionManager mgr, long id)
throws UnknownTransactionException, RemoteException
{
gate();
delegate.abort(mgr, id);
}
public int prepareAndCommit(TransactionManager mgr, long id)
throws UnknownTransactionException, RemoteException
{
gate();
return delegate.prepareAndCommit(mgr, id);
}
public RenewResults renewAll(Uuid[] cookies, long[] durations)
throws RemoteException
{
gate();
return delegate.renewAll(cookies, durations);
}
public Map cancelAll(Uuid[] cookies) throws RemoteException {
gate();
return delegate.cancelAll(cookies);
}
public Object getServiceProxy() throws RemoteException {
gate();
return delegate.getServiceProxy();
}
public JavaSpace space() throws RemoteException {
gate();
return delegate.space();
}
public Uuid contents(EntryRep tmpl, Transaction txn)
throws TransactionException, RemoteException
{
gate();
return delegate.contents(tmpl, txn);
}
public EntryRep[] nextReps(Uuid iterationUuid, int max,
Uuid entryUuid)
throws RemoteException
{
gate();
return delegate.nextReps(iterationUuid, max, entryUuid);
}
public void delete(Uuid iterationUuid, Uuid entryUuid)
throws RemoteException
{
gate();
delegate.delete(iterationUuid, entryUuid);
}
public void close(Uuid iterationUuid) throws RemoteException {
gate();
delegate.close(iterationUuid);
}
public void destroy() throws RemoteException {
gate();
delegate.destroy();
}
public Entry[] getLookupAttributes() throws RemoteException {
gate();
return delegate.getLookupAttributes();
}
public void addLookupAttributes(Entry[] attrSets)
throws RemoteException
{
gate();
delegate.addLookupAttributes(attrSets);
}
public void modifyLookupAttributes(Entry[] attrSetTemplates,
Entry[] attrSets)
throws RemoteException
{
gate();
delegate.modifyLookupAttributes(attrSetTemplates, attrSets);
}
public String[] getLookupGroups() throws RemoteException {
gate();
return delegate.getLookupGroups();
}
public void addLookupGroups(String[] groups) throws RemoteException {
gate();
delegate.addLookupGroups(groups);
}
public void removeLookupGroups(String[] groups) throws RemoteException {
gate();
delegate.removeLookupGroups(groups);
}
public void setLookupGroups(String[] groups) throws RemoteException {
gate();
delegate.setLookupGroups(groups);
}
public LookupLocator[] getLookupLocators() throws RemoteException {
gate();
return delegate.getLookupLocators();
}
public void addLookupLocators(LookupLocator[] locators)
throws RemoteException
{
gate();
delegate.addLookupLocators(locators);
}
public void removeLookupLocators(LookupLocator[] locators)
throws RemoteException
{
gate();
delegate.removeLookupLocators(locators);
}
public void setLookupLocators(LookupLocator[] locators)
throws RemoteException
{
gate();
delegate.setLookupLocators(locators);
}
public Object getProxy() {
// don't need to block or die on this one.
return delegate.getProxy();
}
public TrustVerifier getProxyVerifier() throws RemoteException {
gate();
return delegate.getProxyVerifier();
}
}