Sponsored Link •
|
Lease
object
Lease
Interface
package net.jini.lease; import java.rmi.RemoteException; public interface Lease { /** * Requests a lease that never expires. */ long FOREVER = Long.MAX_VALUE; /** * Requests a lease time most convenient for grantor */ long ANY = -1; /** * Serialized form is a duration - best for between hosts */ int DURATION = 1; /** * Serialized form is absolute */ int ABSOLUTE = 2; /** * Returns absolute time that the lease will expire, * represented as milliseconds from the beginning of the epoch, * relative to the local clock. */ long getExpiration(); /** * Indicates holder no longer interested in resource. */ void cancel() throws UnknownLeaseException, RemoteException; /** * Renews a lease for an additional period of time, * specified in milliseconds. * * This duration is used to determine a new expiration time * for the existing lease. It is not added to the original lease. * * If the renewal fails, the lease is left intact for the same * duration that was in force prior to the call to renew. */ void renew(long duration) throws LeaseDeniedException, UnknownLeaseException, RemoteException; /** * Sets the format to use when serializing the lease. */ void setSerialFormat(int format); /** * Returns the format that will be used to serialize the lease. */ int getSerialFormat(); /** * Creates a Map object that can contain leases whose renewal or * cancellation can be batched, and adds the current lease to that map. * The current lease is put in the map with the duration value given * by the parameter. */ LeaseMap createLeaseMap(long duration); /** * Returns a boolean indicating whether or not the lease given as a * parameter can be batched (placed in the same LeaseMap) with the * current lease. Whether or not two Lease objects can be batched * is an implementation detail determined by the objects. */ boolean canBatch(Lease lease); }
RemoteEventRegistration
RemoteEventListener
notify()
method of registered Event ListenersRemoteEvent
RemoteListener
Interface
package net.jini.event; public interface RemoteEventListener extends java.rmi.Remote, java.util.EventListener { void notify(RemoteEvent theEvent) throws UnknownEventException, java.rmi.RemoteException; }
notify()
is synchronous, so should return quickly
UnknownEventException
if event not recognized
(SPAM)
RemoteEvent
Class
package net.jini.event; import java.rmi.MarshalledObject; public class RemoteEvent extends java.util.EventObject { public RemoteEvent(Object source, long eventID, long seqNum, MarshalledObject handback); public Object getSource(); public long getID(); public long getSequenceNumber(); public MarshalledObject getRegistrationObject(); }
public EventRegistration register(long eventID, MarshalledObject handback, RemoteEventListener toInform, long leaseLength) throws UnknownEventException, RemoteException;
register()
not part of the API, just an example)
eventID
that was obtained somehow from
the EventGenerator
MarshalledObject
that you'll get back in
the RemoteEvent
s
EventRegistration
Class
package net.jini.event; import net.jini.lease.Lease; public class EventRegistration implements java.io.Serializable { public EventRegistration(long eventID, Object source, Lease lease, long seqNum); public long getID(); public Object getSource(); public Lease getLease(); public long getSequenceNumber(); }
TransactionManager
knows only how to manage and
complete transactions
Transaction
object understands transaction semantics
TransactionManager
:TransactionParticipant
:TransactionManager
package net.jini.core.transaction.server; public interface TransactionManager extends Remote, TransactionConstants { /** * Begin a new top level transaction */ Created create() throws LeaseDeniedException, RemoteException; public static class Created implements Serializable { long id; Lease lease; } /** * Join a transaction managed by thisTransactionManager
*/ void join(long id, TransactionParticipant part, long crashCount) throws UnknownTransactionException, CannotJoinException, CrashCountException, RemoteException; /** * Returns the current state of the given transaction. * * Returns any constant inTransactionConstants
: *ABORTED
,ACTIVE
,COMMITTED
,NOTCHANGED
,PREPARED
,VOTING
. */ int getState(long id) throws UnknownTransactionException, RemoteException; /** * Commit the transaction. */ void commit(long id) throws UnknownTransactionException, CannotCommitException, RemoteException; /** * Abort the transaction. */ void abort(long id); throws UnknownTransactionException, CannotAbortException, RemoteException; }
TransactionParticipant
package net.jini.core.transaction.server; public interface TransactionParticipant extends Remote, TransactionConstants { /** * Requests participant prepare itself to commit and vote on * the outcome. ReturnsABORTED
,PREPARED
, orNOTCHANGED
. */ int prepare(TransactionManager mgr, long id) throws UnknownTransactionException, RemoteException; /** * Requests participant make all its PREPARED changes visible outside * the transaction, and unlock any relevant resources. */ int commit(TransactionManager mgr, long id) throws UnknownTransactionException, RemoteException; /** * Requests participant roll back any changes and unlock any * relevant resources. */ int abort(TransactionManager mgr, long id) throws UnknownTransactionException, RemoteException; /** * Used when just one participant left to prepare and all other * participants (if any) have responded with NOTCHANGED. */ int prepareAndCommit(TransactionManager mgr, long id) throws UnknownTransactionException, RemoteException; }
net.jini.core.transaction
TransactionFactory
:Transaction
instancesTransaction
:ServerTransaction
:TransactionFactory
package net.jini.core.transaction; public class TransactionFactory { /** * Create a new top-level transaction. */ public static Transaction.Created create(TransactionManager mgr, long leaseFor) throws LeaseDeniedException, RemoteException; }
Transaction
package net.jini.core.transaction; public interface Transaction { /** * Commit the transation. Manager initiates voting. Returns after * all participants have indicated eitherPREPARED
orNOTCHANGED
. * Else, throwsCannotCommitException
. */ void commit() throws UnknownTransactionException, CannotCommitException, RemoteException; /** * Abort the transaction. Returns as soon as abort decision is * recorded. */ void abort() throws UnknownTransactionException, CannotAbortException, RemoteException; }
ServerTransaction
package net.jini.core.transaction; public class Transaction implements Transaction, Serializable { /** * Join the transaction. */ public void join(TransactionParticipant part, long crashCount) throws UnknownTransactionException, CannotJoinException, CrashCountException, RemoteException; /** * Returns the current state of the transaction. */ public int getState() throws UnknownTransactionException, RemoteException; }
TransactionManager
and lease duration
Transaction
object
and Lease
object
Transaction
to participants when
asking a participant to do a task "under the transaction"
TransactionManager
will instruct all participants to
"roll back"
TransactionManager
will query all participants
TransactionManager
will instruct all participants to
"roll forward"
In the ProgMan/ex1
directory, create a class that implements of the com.artima.fortune.Fortune
interface:
package com.artima.fortune; import java.io.Serializable; public interface Fortune extends Serializable { String getFortune(); }
Name your class whatever you want.
Each time your getFortune()
method is invoked, it should print out a fortune. Use any
fortune that you think might either plausibly appear in a fortune cookie at a Chinese restaurant, or
something you think is funny. We'll let you know if it actually is funny. Your Fortune
object should have at least four fortunes in its repertoire. Each time getFortune()
is
invoked, your method should randomly select one of its fortunes and return it.
Create a Java application named FortuneServer
that performs multicast discovery
and registers your Fortune
service with all discovered lookup services.
You can simply pass an instance of your Fortune
implementation class,
a LookupDiscovery
, and a LeaseRenewalManager
to a JoinManager
.
Something like:
Fortune fortuneService = new FortuneImpl(); String[] groups = new String[] {""}; LookupDiscovery lookupDisc = new LookupDiscovery(groups); JoinManager joinManager = new JoinManager(fortuneService, new Entry[0], new FortuneServer(), lookupDisc, new LeaseRenewalManager());
Make your FortuneServer
class itself implement ServiceIDListener
.
In its serviceIDNotify()
method, print out the service ID assigned by
the lookup service. That way you'll know when your service is successfully registered.
Lastly, make sure that you keep the main thread running so that the JoinManager
will
continue to renew the lease on your service registration. Something like this will serve
that purpose:
while (true) { try { Thread.sleep(1000 * 60); } catch (InterruptedException e) { } }
Start your FortuneServer
and make sure it prints out a service ID. Then leave your
FortuneServer
running, so others will be able to use your Fortune
service.
FortuneClient
whose main()
method just creates an
instance of itself and invokes its own run()
method:
public static void main (String[] args) { FortuneClient fc = new FortuneClient(); fc.run(); }
Create a run()
method that returns void
and takes no arguments. In this method,
set the security manager and perform multicast discovery. Name your discovery listener RegistrarFinder
:
String[] groups = new String[] {""}; LookupDiscovery lookupDisc = new LookupDiscovery(groups); lookupDisc.addDiscoveryListener(new RegistrarFinder());
Define a private inner class named RegistrarFinder
that implements
DiscoveryListener
. DiscoveryListener
should maintain a java.util.Set
of ServiceRegistrar
s currently discovered. As new lookup services are discovered, they should
be added to the Set
. Any existing lookup services that are discarded should be removed from
the Set
.
Test this functionality by printing out a jini:
URL for each lookup service as it is
added and removed from your Set
.
Lastly, enhance your run()
method so that each time the user hits return on the command
line, your method:
Fortune
interface type. This will grab one Fortune
service, chosen randomly from all Fortune
services registered in your chosen lookup
service.
Fortune
service is found, just print out a message to that effect.
Fortune
is returned, invoke getFortune()
on it and
print the result to the standard output.
Test your FortuneClient
by repeatedly hitting return. You should get a random mixture of
fortunes provided by your own Fortune
service and those of your peers.
ChatWriter
from the problem set for the Jini Runtime Infrastructure module so
that it looks for a transaction manager in addition to a JavaSpace. Your ChatWriter
should
wait until both a transactiion manager and the appropriately named JavaSpace is found before it
starts reading lines from the standard input. (Although you look for a particular JavaSpace, you can
accept any transaction manager.)
ChatWriter
class that specify the default room name and
your name, as in:
private static final String roomName = "DefaultRoom"; private static final String userName = "Bill";
Try to pick a name likely to be unique among your peers.
Next, instead of just writing chat messages to the space with a position
value of
Long(0)
, go through the full process outlined in the JavaSpaces lecture for
writing a message to the chat JavaSpace:
The procedure is:
NextMessageNumber
entry for your room
nextMessageNum
field
NextMessageNumber
and write it back to the space
nextMessageNum
,
and the actual message read in from the standard input.
Here's the code snippet from the JavaSpaces lecture:
1 // Create the template for NextMessageNumber 2 NextMessageNumber template = new NextMessageNumber( 3 currentChatRoom, null); 4 5 // Create a transaction 6 Transaction.Created trans = TransactionFactory.create( 7 transMan, TIME_OUT); 8 9 // Renew lease on transaction 10 long expireTime = trans.lease.getExpiration(); 11 Date date = new Date(); 12 long currentTime = date.getTime(); 13 long leaseLen = expireTime - currentTime; 14 if (leaseLen < TIME_OUT) { 15 leaseMan.renewUntil(trans.lease, 16 currentTime + leaseLen, null); 17 } 18 19 // Take the NextMessageNumber entry 20 NextMessageNumber nextMsgNumEntry = 21 (NextMessageNumber) space.take(template, 22 trans.transaction, TIME_OUT); 23 24 // Grab the next message number 25 myMessageNum = nextMsgNumEntry.nextMessageNum; 26 27 // Increment the number in the entry 28 nextMsgNumEntry.increment(); 29 30 // Write the incremented number back to the space 31 write(nextMsgNumEntry, trans.transaction, Lease.FOREVER); 32 33 // Create the ChatMessage entry using the message number 34 ChatMessage msgEntry = new ChatMessage(currentChatRoom, 35 userName, myMessageNum, message); 36 37 // Write the chat message to the space 38 space.write(msgEntry, trans.transaction, MESSAGE_LEASE_LENGTH); 39 40 // Commit the transaction 41 trans.transaction.commit();
Test your revised ChatWriter
with your test JavaSpace. To get a NextMessageNumber
entry written to the space in the first place, feel free to use the WriteEntry
class
from the ProgMan/soln/prob5
directory.
ChatReader
so that it first reads the current NextMessageNumber
entry for the default room. Then, instead of taking all ChatMessage
entries, it just
does a blocking read on the next ChatMessage
that will be written to the space. Simply
fill your ChatMessage
template with the default room and next message number. Each time
the read returns, just increment your local copy of the next message number and do another blocking
read for a ChatMessage
using the new value of next message number.
spaceName
constant in both ChatReader
and
ChatWriter
so that instead of reading from and writing to your test space, you
read from and write to a shared community space. This will allow you to actually chat with
your peers.
Sponsored Links
|