Convert Narya (most of the way) over to a Maven Ant task based build. The
ActionScript bits remain belligerent, but the Java stuff is mostly shipshape. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6222 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.samskivert.util.MethodFinder;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Class related utility functions.
|
||||
*/
|
||||
public class ClassUtil
|
||||
{
|
||||
/**
|
||||
* Locates and returns the first method in the supplied class whose name is equal to the
|
||||
* specified name. If a method is located, it will be cached in the supplied cache so that
|
||||
* subsequent requests will immediately return the method from the cache rather than
|
||||
* re-reflecting.
|
||||
*
|
||||
* @return the method with the specified name or null if no method with that name could be
|
||||
* found.
|
||||
*/
|
||||
public static Method getMethod (String name, Object target, Map<String, Method> cache)
|
||||
{
|
||||
Class<?> tclass = target.getClass();
|
||||
String key = tclass.getName() + ":" + name;
|
||||
Method method = cache.get(key);
|
||||
|
||||
if (method == null) {
|
||||
method = findMethod(tclass, name);
|
||||
if (method != null) {
|
||||
cache.put(key, method);
|
||||
}
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the method on the specified object that has a signature that matches the supplied
|
||||
* arguments array. This is very expensive, so you shouldn't be doing this for something that
|
||||
* happens frequently.
|
||||
*
|
||||
* @return the best matching method with the specified name that accepts the supplied
|
||||
* arguments, or null if no method could be found.
|
||||
*
|
||||
* @see MethodFinder
|
||||
*/
|
||||
public static Method getMethod (String name, Object target, Object[] args)
|
||||
{
|
||||
Class<?> tclass = target.getClass();
|
||||
Method meth = null;
|
||||
|
||||
try {
|
||||
MethodFinder finder = new MethodFinder(tclass);
|
||||
meth = finder.findMethod(name, args);
|
||||
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
// nothing to do here but fall through and return null
|
||||
log.info("No such method", "name", name, "tclass", tclass.getName(), "args", args,
|
||||
"error", nsme);
|
||||
|
||||
} catch (SecurityException se) {
|
||||
log.warning("Unable to look up method?", "tclass", tclass.getName(), "mname", name);
|
||||
}
|
||||
|
||||
return meth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates and returns the first method in the supplied class whose name is equal to the
|
||||
* specified name.
|
||||
*
|
||||
* @return the method with the specified name or null if no method with that name could be
|
||||
* found.
|
||||
*/
|
||||
public static Method findMethod (Class<?> clazz, String name)
|
||||
{
|
||||
Method[] methods = clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(name)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import com.samskivert.util.ResultListener;
|
||||
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.client.InvocationService.ConfirmListener;
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Adapts the response from a {@link ResultListener} to a {@link ConfirmListener} if the failure is
|
||||
* an instance of {@link InvocationException} the message will be passed on to the confirm
|
||||
* listener, otherwise they will be provided with {@link InvocationCodes#INTERNAL_ERROR}.
|
||||
*/
|
||||
public class ConfirmAdapter extends IgnoreConfirmAdapter<Void>
|
||||
{
|
||||
/**
|
||||
* Creates an adapter with the supplied listener.
|
||||
*/
|
||||
public ConfirmAdapter (InvocationService.ConfirmListener listener)
|
||||
{
|
||||
super(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.threerings.io.UnreliableObjectInputStream;
|
||||
import com.threerings.io.UnreliableObjectOutputStream;
|
||||
|
||||
import com.threerings.presents.net.Message;
|
||||
import com.threerings.presents.net.Transport;
|
||||
|
||||
/**
|
||||
* Used on both the client and the server to handle the encoding and decoding of sequenced
|
||||
* datagrams.
|
||||
*/
|
||||
public class DatagramSequencer
|
||||
{
|
||||
/**
|
||||
* Creates a new sequencer that will read and write from the specified streams.
|
||||
*/
|
||||
public DatagramSequencer (UnreliableObjectInputStream uin, UnreliableObjectOutputStream uout)
|
||||
{
|
||||
_uin = uin;
|
||||
_uout = uout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a datagram to the underlying stream.
|
||||
*/
|
||||
public synchronized void writeDatagram (Message datagram)
|
||||
throws IOException
|
||||
{
|
||||
// first write the sequence and acknowledge numbers
|
||||
_uout.writeInt(++_lastNumber);
|
||||
_uout.writeInt(_lastReceived);
|
||||
|
||||
// make sure the mapped sets are clear
|
||||
Set<Class<?>> mappedClasses = _uout.getMappedClasses();
|
||||
mappedClasses.clear();
|
||||
Set<String> mappedInterns = _uout.getMappedInterns();
|
||||
mappedInterns.clear();
|
||||
|
||||
// write the object
|
||||
_uout.writeObject(datagram);
|
||||
|
||||
// if we wrote any class mappings, we will keep them in the send record
|
||||
if (mappedClasses.isEmpty()) {
|
||||
mappedClasses = null;
|
||||
} else {
|
||||
_uout.setMappedClasses(new HashSet<Class<?>>());
|
||||
|
||||
}
|
||||
|
||||
// likewise with the intern mappings
|
||||
if (mappedInterns.isEmpty()) {
|
||||
mappedInterns = null;
|
||||
} else {
|
||||
_uout.setMappedInterns(new HashSet<String>());
|
||||
|
||||
}
|
||||
|
||||
// record the transmission
|
||||
_sendrecs.add(new SendRecord(_lastNumber, mappedClasses, mappedInterns));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a datagram from the underlying stream.
|
||||
*
|
||||
* @return the contents of the datagram, or <code>null</code> if the datagram was received
|
||||
* out-of-order.
|
||||
*/
|
||||
public synchronized Message readDatagram ()
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
// read in the sequence number and determine if it's out-of-order
|
||||
int number = _uin.readInt();
|
||||
if (number <= _lastReceived) {
|
||||
return null;
|
||||
}
|
||||
_missedCount = number - _lastReceived - 1;
|
||||
_lastReceived = number;
|
||||
|
||||
// read the acknowledge number and process all send records up to that one
|
||||
int received = _uin.readInt();
|
||||
int remove = 0;
|
||||
for (int ii = 0, nn = _sendrecs.size(); ii < nn; ii++) {
|
||||
SendRecord sendrec = _sendrecs.get(ii);
|
||||
if (sendrec.number > received) {
|
||||
break;
|
||||
}
|
||||
remove++;
|
||||
if (sendrec.number == received) {
|
||||
if (sendrec.mappedClasses != null) {
|
||||
_uout.noteClassMappingsReceived(sendrec.mappedClasses);
|
||||
}
|
||||
if (sendrec.mappedInterns != null) {
|
||||
_uout.noteInternMappingsReceived(sendrec.mappedInterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remove > 0) {
|
||||
_sendrecs.subList(0, remove).clear();
|
||||
}
|
||||
|
||||
// read the contents of the datagram, note the transport, and return
|
||||
Message datagram = (Message)_uin.readObject();
|
||||
datagram.setTransport(Transport.UNRELIABLE_ORDERED);
|
||||
return datagram;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of datagrams missed between the last and the one before it.
|
||||
*/
|
||||
public int getMissedCount ()
|
||||
{
|
||||
return _missedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* A record of a sent datagram.
|
||||
*/
|
||||
protected static class SendRecord
|
||||
{
|
||||
/** The sequence number of the datagram. */
|
||||
public int number;
|
||||
|
||||
/** The set of classes for which mappings were included in the datagram (or
|
||||
* <code>null</code> for none). */
|
||||
public Set<Class<?>> mappedClasses;
|
||||
|
||||
/** The set of interns for which mappings were included in the datagram (or
|
||||
* <code>null</code> for none). */
|
||||
public Set<String> mappedInterns;
|
||||
|
||||
public SendRecord (int number, Set<Class<?>> mappedClasses, Set<String> mappedInterns)
|
||||
{
|
||||
this.number = number;
|
||||
this.mappedClasses = mappedClasses;
|
||||
this.mappedInterns = mappedInterns;
|
||||
}
|
||||
}
|
||||
|
||||
/** The underlying input stream. */
|
||||
protected UnreliableObjectInputStream _uin;
|
||||
|
||||
/** The underlying output stream. */
|
||||
protected UnreliableObjectOutputStream _uout;
|
||||
|
||||
/** The last sequence number written. */
|
||||
protected int _lastNumber;
|
||||
|
||||
/** The most recent sequence number received. */
|
||||
protected int _lastReceived;
|
||||
|
||||
/** The number of datagrams missed between the last and the one before it. */
|
||||
protected int _missedCount;
|
||||
|
||||
/** Records of datagrams sent. */
|
||||
protected ArrayList<SendRecord> _sendrecs = Lists.newArrayList();
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
|
||||
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Provides the result of an invocation service request as a future result. Modeled on and mostly
|
||||
* implemented by code from {@link FutureTask}.
|
||||
*/
|
||||
public class FutureResult<V>
|
||||
implements Future<V>, InvocationService.ResultListener
|
||||
{
|
||||
// from interface Future
|
||||
public boolean cancel (boolean mayInterruptIfRunning)
|
||||
{
|
||||
return _sync.innerCancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
// from interface Future
|
||||
public boolean isCancelled ()
|
||||
{
|
||||
return _sync.innerIsCancelled();
|
||||
}
|
||||
|
||||
// from interface Future
|
||||
public boolean isDone ()
|
||||
{
|
||||
return _sync.innerIsDone();
|
||||
}
|
||||
|
||||
// from interface Future
|
||||
public V get () throws InterruptedException, ExecutionException
|
||||
{
|
||||
return _sync.innerGet();
|
||||
}
|
||||
|
||||
// from interface Future
|
||||
public V get (long timeout, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
return _sync.innerGet(unit.toNanos(timeout));
|
||||
}
|
||||
|
||||
// from interface InvocationService.ResultListener
|
||||
public void requestProcessed (Object result)
|
||||
{
|
||||
@SuppressWarnings("unchecked") V value = (V)result;
|
||||
_sync.innerSet(value);
|
||||
}
|
||||
|
||||
// from interface InvocationService.ResultListener
|
||||
public void requestFailed (String cause)
|
||||
{
|
||||
_sync.innerSetException(new InvocationException(cause));
|
||||
}
|
||||
|
||||
protected class Sync extends AbstractQueuedSynchronizer
|
||||
{
|
||||
public boolean innerIsCancelled () {
|
||||
return getState() == CANCELLED;
|
||||
}
|
||||
|
||||
public boolean innerIsDone () {
|
||||
return ranOrCancelled(getState());
|
||||
}
|
||||
|
||||
public V innerGet() throws InterruptedException, ExecutionException {
|
||||
acquireSharedInterruptibly(0);
|
||||
if (getState() == CANCELLED) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
if (_exception != null) {
|
||||
throw new ExecutionException(_exception);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
public V innerGet (long nanosTimeout)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
if (!tryAcquireSharedNanos(0, nanosTimeout)) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
if (getState() == CANCELLED) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
if (_exception != null) {
|
||||
throw new ExecutionException(_exception);
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
|
||||
public void innerSet (V v) {
|
||||
for (;;) {
|
||||
int s = getState();
|
||||
if (s == RAN) {
|
||||
return;
|
||||
}
|
||||
if (s == CANCELLED) {
|
||||
releaseShared(0);
|
||||
return;
|
||||
}
|
||||
if (compareAndSetState(s, RAN)) {
|
||||
_result = v;
|
||||
releaseShared(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void innerSetException (Throwable t) {
|
||||
for (;;) {
|
||||
int s = getState();
|
||||
if (s == RAN) {
|
||||
return;
|
||||
}
|
||||
if (s == CANCELLED) {
|
||||
releaseShared(0);
|
||||
return;
|
||||
}
|
||||
if (compareAndSetState(s, RAN)) {
|
||||
_exception = t;
|
||||
_result = null;
|
||||
releaseShared(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean innerCancel (boolean mayInterruptIfRunning) {
|
||||
for (;;) {
|
||||
int s = getState();
|
||||
if (ranOrCancelled(s)) {
|
||||
return false;
|
||||
}
|
||||
if (compareAndSetState(s, CANCELLED)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
releaseShared(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override protected int tryAcquireShared (int ignore) {
|
||||
return innerIsDone()? 1 : -1;
|
||||
}
|
||||
|
||||
@Override protected boolean tryReleaseShared (int ignore) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean ranOrCancelled (int state) {
|
||||
return (state & (RAN | CANCELLED)) != 0;
|
||||
}
|
||||
|
||||
/** The result to return from get() */
|
||||
protected V _result;
|
||||
|
||||
/** The exception to throw from get() */
|
||||
protected Throwable _exception;
|
||||
|
||||
protected static final int RUNNING = 1;
|
||||
protected static final int RAN = 2;
|
||||
protected static final int CANCELLED = 4;
|
||||
}
|
||||
|
||||
protected Sync _sync = new Sync();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import com.samskivert.util.ResultListener;
|
||||
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.client.InvocationService.ConfirmListener;
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Adapts the response from a {@link ResultListener} to a {@link ConfirmListener} wherein the
|
||||
* result is ignored. If the failure is an instance fo {@link InvocationException} the message will
|
||||
* be passed on to the confirm listener, otherwise they will be provided with {@link
|
||||
* InvocationCodes#INTERNAL_ERROR}.
|
||||
*
|
||||
* @param <T> the type of result expected by the listener.
|
||||
*/
|
||||
public class IgnoreConfirmAdapter<T> implements ResultListener<T>
|
||||
{
|
||||
/**
|
||||
* Creates an adapter with the supplied listener.
|
||||
*/
|
||||
public IgnoreConfirmAdapter (InvocationService.ConfirmListener listener)
|
||||
{
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestCompleted (T result)
|
||||
{
|
||||
_listener.requestProcessed();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (Exception cause)
|
||||
{
|
||||
if (cause instanceof InvocationException) {
|
||||
_listener.requestFailed(cause.getMessage());
|
||||
} else {
|
||||
log.warning(cause);
|
||||
_listener.requestFailed(InvocationCodes.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
protected InvocationService.ConfirmListener _listener;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import com.samskivert.util.ResultListener;
|
||||
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Adapts the response from a {@link InvocationService.ResultListener} to a {@link ResultListener}.
|
||||
* In the event of failure, the failure string is wrapped in an {@link InvocationException}.
|
||||
*/
|
||||
public class InvocationAdapter implements InvocationService.ResultListener
|
||||
{
|
||||
public InvocationAdapter (ResultListener<Object> target)
|
||||
{
|
||||
_target = target;
|
||||
}
|
||||
|
||||
// from InvocationService.ResultListener
|
||||
public void requestProcessed (Object result)
|
||||
{
|
||||
_target.requestCompleted(result);
|
||||
}
|
||||
|
||||
// from InvocationService.ResultListener
|
||||
public void requestFailed (String cause)
|
||||
{
|
||||
_target.requestFailed(new InvocationException(cause));
|
||||
}
|
||||
|
||||
protected ResultListener<Object> _target;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
import com.samskivert.util.Invoker;
|
||||
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Simplifies a common pattern which is to post an {@link Invoker} unit which does some database
|
||||
* operation and then calls back to an {@link InvocationService.InvocationListener} of some
|
||||
* sort. If the database operation fails, the error will be logged and the result listener will be
|
||||
* replied to with {@link InvocationCodes#INTERNAL_ERROR}.
|
||||
*/
|
||||
public abstract class PersistingUnit extends Invoker.Unit
|
||||
{
|
||||
public PersistingUnit (InvocationService.InvocationListener listener)
|
||||
{
|
||||
this("UnknownPersistingUnit", listener);
|
||||
}
|
||||
|
||||
public PersistingUnit (String name, InvocationService.InvocationListener listener)
|
||||
{
|
||||
super(name);
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a persisting unit with the supplied name and listener and a set of key/value pairs
|
||||
* that will be included in the failure message if this unit fails.
|
||||
*/
|
||||
public PersistingUnit (String name, InvocationService.InvocationListener listener,
|
||||
Object... args)
|
||||
{
|
||||
super(name);
|
||||
if (args != null && args.length % 2 == 1) {
|
||||
throw new IllegalArgumentException("Missing key or value in key/value pair arguments.");
|
||||
}
|
||||
_listener = listener;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is where the unit performs its persistent actions. Any persistence exception
|
||||
* will be caught and logged along with the output from {@link #getFailureMessage}, if any.
|
||||
*/
|
||||
public abstract void invokePersistent ()
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Handles the success case, which by default posts a response to a ConfirmListener.
|
||||
* If you need something else, or to repond to a ResultListener, you'll need to override this.
|
||||
*/
|
||||
public void handleSuccess ()
|
||||
{
|
||||
if (_listener instanceof InvocationService.ConfirmListener) {
|
||||
reportRequestProcessed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the failure case by logging the error and reporting an internal error to the
|
||||
* listener.
|
||||
*/
|
||||
public void handleFailure (Exception error)
|
||||
{
|
||||
if (error instanceof InvocationException) {
|
||||
_listener.requestFailed(error.getMessage());
|
||||
} else {
|
||||
if (_args != null) {
|
||||
log.warning(getFailureMessage(), ArrayUtil.append(_args, error));
|
||||
} else {
|
||||
log.warning(getFailureMessage(), error);
|
||||
}
|
||||
_listener.requestFailed(InvocationCodes.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from Invoker.Unit
|
||||
public boolean invoke ()
|
||||
{
|
||||
try {
|
||||
invokePersistent();
|
||||
} catch (Exception pe) {
|
||||
_error = pe;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // from Invoker.Unit
|
||||
public void handleResult ()
|
||||
{
|
||||
if (_error != null) {
|
||||
handleFailure(_error);
|
||||
} else {
|
||||
handleSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the listener is known to be a ConfirmListener, this will cast it and report that the
|
||||
* request was processed.
|
||||
*/
|
||||
protected void reportRequestProcessed ()
|
||||
{
|
||||
((InvocationService.ConfirmListener)_listener).requestProcessed();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the listener is known to be a ResultListener, this will cast it and report that the
|
||||
* request was processed.
|
||||
*/
|
||||
protected void reportRequestProcessed (Object result)
|
||||
{
|
||||
((InvocationService.ResultListener)_listener).requestProcessed(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a custom failure message in the event that the persistent action fails. This will
|
||||
* be logged along with the exception.
|
||||
*/
|
||||
protected String getFailureMessage ()
|
||||
{
|
||||
return this + " failed.";
|
||||
}
|
||||
|
||||
protected InvocationService.InvocationListener _listener;
|
||||
protected Exception _error;
|
||||
protected Object[] _args;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import com.samskivert.util.Config;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
|
||||
/**
|
||||
* Provides access to standard services needed by code that is part of or uses the Presents
|
||||
* package.
|
||||
*/
|
||||
public interface PresentsContext
|
||||
{
|
||||
/**
|
||||
* Provides a configuration object from which various services can obtain configuration values
|
||||
* and via the properties file that forms the basis of the configuration object, those services
|
||||
* can be customized.
|
||||
*/
|
||||
Config getConfig ();
|
||||
|
||||
/**
|
||||
* Returns a reference to the client. This reference should be valid for the life of the
|
||||
* application.
|
||||
*/
|
||||
Client getClient ();
|
||||
|
||||
/**
|
||||
* Returns a reference to the distributed object manager. This reference is only valid for the
|
||||
* duration of a session.
|
||||
*/
|
||||
DObjectManager getDObjectManager ();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import com.samskivert.util.ResultListener;
|
||||
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* Adapts the response from a {@link ResultListener} to an InvocationService.ResultListener if the
|
||||
* failure is an instance of {@link InvocationException} the message will be passed on to the
|
||||
* result listener, otherwise they will be provided with {@link InvocationCodes#INTERNAL_ERROR}.
|
||||
*
|
||||
* @param <T> the type of result expected by the listener.
|
||||
*/
|
||||
public class ResultAdapter<T> implements ResultListener<T>
|
||||
{
|
||||
/**
|
||||
* Creates an adapter with the supplied listener.
|
||||
*/
|
||||
public ResultAdapter (InvocationService.ResultListener listener)
|
||||
{
|
||||
_listener = listener;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestCompleted (T result)
|
||||
{
|
||||
_listener.requestProcessed(result);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (Exception cause)
|
||||
{
|
||||
if (cause instanceof InvocationException) {
|
||||
_listener.requestFailed(cause.getMessage());
|
||||
} else {
|
||||
log.warning(cause);
|
||||
_listener.requestFailed(InvocationCodes.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
protected InvocationService.ResultListener _listener;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* Maintains a list of result listeners, dispatching the eventual actual result or failure to them
|
||||
* all as if they were a single listener.
|
||||
*/
|
||||
public class ResultListenerList extends ArrayList<InvocationService.ResultListener>
|
||||
implements InvocationService.ResultListener
|
||||
{
|
||||
// from InvocationService.ResultListener
|
||||
public void requestProcessed (Object result)
|
||||
{
|
||||
for (InvocationService.ResultListener listener : this) {
|
||||
listener.requestProcessed(result);
|
||||
}
|
||||
}
|
||||
|
||||
// from InvocationService.ResultListener
|
||||
public void requestFailed (String cause)
|
||||
{
|
||||
for (InvocationService.ResultListener listener : this) {
|
||||
listener.requestFailed(cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.presents.util;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.dobj.ChangeListener;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
|
||||
import static com.threerings.presents.Log.log;
|
||||
|
||||
/**
|
||||
* A class that safely handles the asynchronous subscription to a distributed object when it is
|
||||
* not know if the subscription will complete before the subscriber decides they no longer wish to
|
||||
* be subscribed.
|
||||
*
|
||||
* @param <T> the type of object to which we are subscribing.
|
||||
*/
|
||||
public class SafeSubscriber<T extends DObject>
|
||||
implements Subscriber<T>
|
||||
{
|
||||
/**
|
||||
* Creates a safe subscriber for the specified distributed object which will interact with the
|
||||
* specified subscriber. If any listeners are given, they'll be added as listeners to the
|
||||
* distributed object after the subscriber is told it's available, and will be removed when
|
||||
* unsubscribing from the object.
|
||||
*/
|
||||
public SafeSubscriber (int oid, Subscriber<T> subscriber, ChangeListener...listeners)
|
||||
{
|
||||
// make sure they're not fucking us around
|
||||
if (oid <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid oid provided to safesub [oid=" + oid + "]");
|
||||
}
|
||||
if (subscriber == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Null subscriber provided to safesub [oid=" + oid + "]");
|
||||
}
|
||||
|
||||
_oid = oid;
|
||||
_subscriber = subscriber;
|
||||
_listeners = listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we are currently subscribed to our object (or in
|
||||
* the process of obtaining a subscription).
|
||||
*/
|
||||
public boolean isActive ()
|
||||
{
|
||||
return _active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the subscription process.
|
||||
*/
|
||||
public void subscribe (DObjectManager omgr)
|
||||
{
|
||||
if (_active) {
|
||||
log.warning("Active safesub asked to resubscribe " + this + ".", new Exception());
|
||||
return;
|
||||
}
|
||||
|
||||
// note that we are now again in the "wishing to be subscribed" state
|
||||
_active = true;
|
||||
|
||||
// make sure we dont have an object reference (which should be
|
||||
// logically impossible)
|
||||
if (_object != null) {
|
||||
log.warning("Incroyable! A safesub has an object and was " +
|
||||
"non-active!? " + this + ".", new Exception());
|
||||
// make do in the face of insanity
|
||||
_subscriber.objectAvailable(_object);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_pending) {
|
||||
// we were previously asked to subscribe, then they asked to
|
||||
// unsubscribe and now they've asked to subscribe again, all
|
||||
// before the original subscription even completed; we need do
|
||||
// nothing here except as the original subscription request
|
||||
// will eventually come through and all will be well
|
||||
return;
|
||||
}
|
||||
|
||||
// we're not pending and we just became active, that means we need
|
||||
// to request to subscribe to our object
|
||||
_pending = true;
|
||||
omgr.subscribeToObject(_oid, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the object subscription. If the initial subscription has
|
||||
* not yet completed, the desire to terminate will be noted and the
|
||||
* subscription will be terminated as soon as it completes.
|
||||
*/
|
||||
public void unsubscribe (DObjectManager omgr)
|
||||
{
|
||||
if (!_active) {
|
||||
// we may be non-active and have no object which could mean
|
||||
// that subscription failed; in which case we don't want to
|
||||
// complain about anything, just quietly ignore the
|
||||
// unsubscribe request
|
||||
if (_object == null && !_pending) {
|
||||
return;
|
||||
}
|
||||
log.warning("Inactive safesub asked to unsubscribe " + this + ".", new Exception());
|
||||
}
|
||||
|
||||
// note that we no longer desire to be subscribed
|
||||
_active = false;
|
||||
|
||||
if (_pending) {
|
||||
// make sure we don't have an object reference
|
||||
if (_object != null) {
|
||||
log.warning("Incroyable! A safesub has an object and is " +
|
||||
"pending!? " + this + ".", new Exception());
|
||||
} else {
|
||||
// nothing to do but wait for the subscription to complete
|
||||
// at which point we'll pitch the object post-haste
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have our object
|
||||
if (_object == null) {
|
||||
log.warning("Zut alors! A safesub _was_ active and not " +
|
||||
"pending yet has no object!? " + this + ".", new Exception());
|
||||
// nothing to do since we're apparently already unsubscribed
|
||||
return;
|
||||
}
|
||||
|
||||
// clear out any listeners we added
|
||||
for (ChangeListener listener : _listeners) {
|
||||
_object.removeListener(listener);
|
||||
}
|
||||
|
||||
// finally effect our unsubscription
|
||||
_object = null;
|
||||
omgr.unsubscribeFromObject(_oid, this);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void objectAvailable (T object)
|
||||
{
|
||||
// make sure life is not too cruel
|
||||
if (_object != null) {
|
||||
log.warning("Madre de dios! Our object came available but " +
|
||||
"we've already got one!? " + this);
|
||||
// go ahead and pitch the old one, God knows what's going on
|
||||
_object = null;
|
||||
}
|
||||
if (!_pending) {
|
||||
log.warning("J.C. on a pogo stick! Our object came available " +
|
||||
"but we're not pending!? " + this);
|
||||
// go with our badselves, it's the only way
|
||||
}
|
||||
|
||||
// we're no longer pending
|
||||
_pending = false;
|
||||
|
||||
// if we are no longer active, we don't want this damned thing
|
||||
if (!_active) {
|
||||
DObjectManager omgr = object.getManager();
|
||||
// if the object's manager is null, that means the object is
|
||||
// already destroyed and we need not trouble ourselves with
|
||||
// unsubscription as it has already been pitched to the dogs
|
||||
if (omgr != null) {
|
||||
omgr.unsubscribeFromObject(_oid, this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise the air is fresh and clean and we can do our job
|
||||
_object = object;
|
||||
_subscriber.objectAvailable(object);
|
||||
for (ChangeListener listener : _listeners) {
|
||||
_object.addListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void requestFailed (int oid, ObjectAccessException cause)
|
||||
{
|
||||
// do the right thing with our pending state
|
||||
if (!_pending) {
|
||||
log.warning("Criminy creole! Our subscribe failed but we're " +
|
||||
"not pending!? " + this);
|
||||
// go with our badselves, it's the only way
|
||||
}
|
||||
_pending = false;
|
||||
|
||||
// if we're active, let our subscriber know that the shit hit the fan
|
||||
if (_active) {
|
||||
// deactivate ourselves as we never got our object (and thus
|
||||
// the real subscriber need not call unsubscribe())
|
||||
_active = false;
|
||||
_subscriber.requestFailed(oid, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return "[oid=" + _oid +
|
||||
", sub=" + StringUtil.shortClassName(_subscriber) +
|
||||
", active=" + _active + ", pending=" + _pending +
|
||||
", dobj=" + StringUtil.shortClassName(_object) + "]";
|
||||
}
|
||||
|
||||
protected ChangeListener[] _listeners;
|
||||
protected int _oid;
|
||||
protected Subscriber<T> _subscriber;
|
||||
protected T _object;
|
||||
protected boolean _active;
|
||||
protected boolean _pending;
|
||||
}
|
||||
Reference in New Issue
Block a user