|
|
|
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);
}
RemoteEventRegistrationRemoteEventListenernotify()
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 RemoteEvents
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 this TransactionManager
*/
void join(long id, TransactionParticipant part, long crashCount)
throws UnknownTransactionException, CannotJoinException,
CrashCountException, RemoteException;
/**
* Returns the current state of the given transaction.
*
* Returns any constant in TransactionConstants:
* 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. Returns ABORTED, PREPARED, or NOTCHANGED.
*/
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 either PREPARED or NOTCHANGED.
* Else, throws CannotCommitException.
*/
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 ServiceRegistrars 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
|