View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.river.norm;
19  
20  import org.apache.river.landlord.LeasedResource;
21  import org.apache.river.norm.event.EventFactory;
22  import org.apache.river.norm.event.EventType;
23  import org.apache.river.norm.event.EventTypeGenerator;
24  import org.apache.river.norm.event.SendMonitor;
25  import org.apache.river.norm.proxy.*;
26  import java.io.IOException;
27  import java.io.InvalidObjectException;
28  import java.io.ObjectInputStream;
29  import java.io.Serializable;
30  import java.rmi.MarshalledObject;
31  import java.security.AccessControlContext;
32  import java.util.ArrayList;
33  import java.util.Collections;
34  import java.util.HashSet;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Set;
38  import net.jini.core.event.EventRegistration;
39  import net.jini.core.event.RemoteEvent;
40  import net.jini.core.event.RemoteEventListener;
41  import net.jini.core.lease.Lease;
42  import net.jini.export.ProxyAccessor;
43  import net.jini.id.Uuid;
44  import net.jini.io.MarshalledInstance;
45  import net.jini.lease.ExpirationWarningEvent;
46  import net.jini.lease.LeaseRenewalSet;
47  import net.jini.security.ProxyPreparer;
48  import org.apache.river.api.io.AtomicSerial;
49  import org.apache.river.api.io.AtomicSerial.GetArg;
50  
51  /**
52   * Norm's internal representation of LeaseRenewalSets.  Unless otherwise
53   * noted the methods of this class assume synchronization being handled
54   * by the caller.
55   *
56   * @author Sun Microsystems, Inc.
57   */
58  @AtomicSerial
59  class LeaseSet implements Serializable, LeasedResource {
60      private static final long serialVersionUID = 2;
61  
62      /**
63       * Expiration time of the set
64       * @serial
65       */
66      private long expiration;
67  
68      /**
69       * We keep two copies of the expiration time guarded by different 
70       * locks so the the client lease renewal threads don't have to wait
71       * on the set's lock.
72       */
73      private transient ExpirationTime expiration2;
74    
75      /**
76       * ID that uniquely identifies this set
77       * @serial
78       */
79      private final Uuid ID;
80  
81      /**
82       * A collection of the client leases in this set (in wrapped form).
83       * @serial
84       */ 
85      private final Set<ClientLeaseWrapper> leases;
86  
87      /**
88       * A table that maps client leases to client lease wrappers.
89       */
90      private volatile transient LeaseTable leaseTable;
91  
92      /**
93       * Time before <code>expiration</code> to send expiration warning events
94       * @serial
95       */
96      private volatile long minWarning = NormServer.NO_LISTENER;
97  
98      /**
99       * The event type for expiration warning events
100      * @serial
101      */
102     private EventType warningEventType;
103 
104     /**
105      * The current sequence number for expiration warning events
106      * @serial
107      */
108     private volatile long warningSeqNum;
109 
110     /**
111      * The event type for failure events
112      * @serial
113      */
114     private EventType failureEventType;
115 
116     /**
117      * <code>PersistentStore</code> that changes should be logged to.
118      */
119     private transient PersistentStore store;
120 
121     /**
122      * The <code>NormServerBaseImpl</code> are attached to
123      */
124     private transient NormServerBaseImpl normServerBaseImpl;
125 
126     // Constructors and state restoration
127     /**
128      * Simple constructor.  Note expiration will be set when we allocate
129      * a lease for this set.
130      * @param ID for this set
131      * @param generator object set can use to create new event type objects
132      * @param store <code>PersistentStore<code> that changes should be logged
133      *              to
134      * @param normServerBaseImpl the <code>NormServerBaseImpl</code> that 
135      *              created this set
136      */
137     LeaseSet(Uuid ID,
138 	    EventTypeGenerator generator,
139 	    PersistentStore store,
140 	    NormServerBaseImpl normServerBaseImpl,
141 	    AccessControlContext context)
142     {
143 	this.leases = new HashSet<ClientLeaseWrapper>();
144 	this.store = store;
145 	this.normServerBaseImpl = normServerBaseImpl;
146 	this.ID = ID;
147 
148 	// For completeness
149         expiration = 0;
150 	expiration2 = new ExpirationTime(expiration);
151 	leaseTable = new LeaseTable();
152 
153 	final SendMonitor sendMonitor = 
154 	    normServerBaseImpl.newSendMonitor(this);	
155 
156 	try {
157 	    warningEventType = 
158 		generator.newEventType(sendMonitor, 
159 			LeaseRenewalSet.EXPIRATION_WARNING_EVENT_ID, context);
160 	    failureEventType =
161 		generator.newEventType(sendMonitor, 
162 			LeaseRenewalSet.RENEWAL_FAILURE_EVENT_ID, context);
163 	} catch (IOException e) {
164 	    // Because we are passing null for the listener we will
165 	    // never get an exception
166 	    throw new AssertionError();
167 	}
168     }
169 
170     LeaseSet(GetArg arg) throws IOException {
171 	this(	arg.get("expiration", 0L),
172 		(Uuid) arg.get("ID", null),
173 		check((Set<ClientLeaseWrapper>) arg.get("leases", null)),
174 		arg.get("minWarning", 0L),
175 		(EventType) arg.get("warningEventType", null),
176 		arg.get("warningSeqNum", 0L),
177 		(EventType) arg.get("failureEventType", null)
178 		);
179 	// Restore the 2nd copy
180 	expiration2 = new ExpirationTime(expiration);
181 
182 	// Create lease table, but only populate after restoring lease wrapper
183 	// transient state
184 	leaseTable = new LeaseTable();
185     }
186     
187     LeaseSet(long expiration,
188 	    Uuid ID,
189 	    Set leases,
190 	    long minWarning,
191 	    EventType warningEventType,
192 	    long warningSeqNum,
193 	    EventType failureEventType)
194     {
195 	this.expiration = expiration;
196 	this.ID = ID;
197 	this.leases = new HashSet<ClientLeaseWrapper>(leases);
198 	this.minWarning = minWarning;
199 	this.warningEventType = warningEventType;
200 	this.warningSeqNum = warningSeqNum;
201 	this.failureEventType = failureEventType;
202     }
203     
204     private static Set<ClientLeaseWrapper> check(Set<ClientLeaseWrapper> leases) 
205 	    throws InvalidObjectException
206     {
207 	try {
208 	    Set<ClientLeaseWrapper> checkLeases = Collections.checkedSet(
209 		    new HashSet<ClientLeaseWrapper>(leases.size()), ClientLeaseWrapper.class);
210 	    checkLeases.addAll(leases);
211 	    return checkLeases;
212 	} catch (ClassCastException e){
213 	    InvalidObjectException ex = new InvalidObjectException(
214 		    "leases must only contain instances of ClientLeaseWrapper");
215 	    ex.initCause(e);
216 	    throw ex;
217 	}
218     }
219 
220     /**
221      * Override readObject so we can restore expiration2
222      */
223     private void readObject(ObjectInputStream in) 
224 	throws IOException, ClassNotFoundException 
225     {
226 	in.defaultReadObject();
227 	synchronized (this){
228 	    // Restore the 2nd copy
229 	    expiration2 = new ExpirationTime(expiration);
230 
231 	    // Create lease table, but only populate after restoring lease wrapper
232 	    // transient state
233 	    leaseTable = new LeaseTable();
234 	}
235     }
236 
237     /** 
238      * Restore the transient state of the set that can't be restored 
239      * automatically after a log recovery.
240      * @param generator event type generator associated with this set's events
241      * @param store <code>PersistentStore<code> that changes should be 
242      *        logged to
243      * @param normServerBaseImpl the <code>normServerBaseImpl</code> that 
244      *        created this set
245      * @param recoveredListenerPreparer the proxy preparer to use to prepare
246      *	      recovered listeners
247      * @return an iterator over the set of client leases
248      */
249     Iterator restoreTransientState(EventTypeGenerator generator, 
250 				    PersistentStore store,
251 				    NormServerBaseImpl normServerBaseImpl,
252 				    ProxyPreparer recoveredListenerPreparer,
253 				    AccessControlContext context)
254     {
255 	this.normServerBaseImpl = normServerBaseImpl;
256 	this.store = store;
257 
258 	final SendMonitor sendMonitor = 
259 	    normServerBaseImpl.newSendMonitor(this);	
260 	warningEventType.restoreTransientState(
261 		generator, sendMonitor, recoveredListenerPreparer, context);
262 	failureEventType.restoreTransientState(
263 		generator, sendMonitor, recoveredListenerPreparer, context);
264 
265 	// Instead of logging the sequence number each time we send
266 	// a warning event (like we do for renewal failures), we just
267 	// record the current sequence number as of every snapshot and
268 	// assume we won't send more than Integer.MAX_VALUE warning events
269 	// between snapshots, nor will we crash and restart more than
270 	// ~ Long.MAX_VALUE/Integer.MAX_VALUE times.
271 	warningEventType.setLastSequenceNumber(warningSeqNum +
272 					       Integer.MAX_VALUE);
273 	normServerBaseImpl.updateLeaseCount(leases.size());
274 	return leases.iterator();
275     }
276 
277     /**
278      * Return the wrapper for the specified client lease, or null if not
279      * found.
280      */
281     ClientLeaseWrapper getClientLeaseWrapper(Lease clientLease) {
282 	return leaseTable.get(clientLease);
283     }
284 
285     /**
286      * Utility method to replace a client lease in the lease set.  Returns true
287      * if an equal lease was not already present.  Does not update the lease
288      * table.
289      */
290     private boolean replace(ClientLeaseWrapper clw) {
291 	boolean found = leases.remove(clw);
292 	leases.add(clw);
293 	return !found;
294     }
295 
296     /**
297      * Add or update the specified wrapped client lease to the set.
298      * @param clw lease to be added or updated
299      */
300     void update(ClientLeaseWrapper clw) {
301 	boolean added = replace(clw);
302 	leaseTable.put(clw);
303 	final Object u = new UpdateClientLease(this, clw);
304 	store.update(u);
305 	if (added) {
306 	    normServerBaseImpl.updateLeaseCount(1);
307 	}
308     }
309 
310     /**
311      * Add a lease already in the lease set to the lease table.  Used during
312      * recovery to add the wrapper to the lease table only after it has been
313      * given its recovery proxy preparer and attempting to unpack the lease has
314      * attempted.
315      */
316     void addToLeaseTable(ClientLeaseWrapper clw) {
317 	leaseTable.put(clw);
318     }
319 
320     /**
321      * Return true if the passed wrapper is in the set 
322      */
323     boolean doesContainWrapper(ClientLeaseWrapper clw) {
324 	return leases.contains(clw);
325     }
326 
327     /**
328      * Utility method to remove a client lease.  Returns true if the lease is
329      * removed.
330      */
331     private boolean removeInternal(ClientLeaseWrapper clw) {
332 	if (leases.remove(clw)) {
333 	    leaseTable.remove(clw);
334 	    return true;
335 	}
336 	return false;
337     }
338 
339     /**
340      * Remove the specified wrapped client lease from the set.
341      * @param clw lease to removed
342      * @return false if the lease has already been removed, or 
343      * if logically the lease is no longer in the set (e.g. its
344      * membership expiration has been reached)
345      */
346     boolean remove(ClientLeaseWrapper clw) {
347 	if (!removeInternal(clw)) {
348 	    // if we are here the lease must have already been 
349 	    // removed by some other thread, don't bother logging
350 	    return false;
351 	}
352 
353 	// Even if the membership expiration was expired we still
354 	// want to update the log
355 	final Object u = new RemoveClientLease(this, clw);
356 	store.update(u);
357 	normServerBaseImpl.updateLeaseCount(-1);
358 
359 	return (clw.getMembershipExpiration() > System.currentTimeMillis());
360     }
361 
362     /**
363      * Destroy a lease set
364      * @return a <code>Set</code> with all of the sets WrappedClientLeases
365      */
366     Set destroy() {
367 	setExpiration(-1);
368 	final Object u = new CancelLeaseSet(getUuid());
369 	store.update(u);
370 	normServerBaseImpl.updateLeaseCount(-leases.size());
371 	return leases;
372     }
373 
374     /**
375      * Return an array of leases in marshalled form.  For each lease we have
376      * in unmarshalled form serialize using the duration format, for each
377      * lease we can't unmarshal just use the MarshalledInstance we have on
378      * hand.  If the set is empty return null.
379      */
380     MarshalledInstance[] getLeases() {
381 	final long now = System.currentTimeMillis();
382 	final Iterator i = leases.iterator();
383 	final List l = new ArrayList(leases.size());
384 
385 	while (i.hasNext()) {
386 	    final ClientLeaseWrapper clw = (ClientLeaseWrapper) i.next();
387 
388 	    // Check to make sure the leases membership expiration has not 
389 	    // passed
390 	    if (now > clw.getMembershipExpiration())
391 		continue;
392 
393 	    l.add(clw.getMarshalledClientLease());
394 	}
395 
396 	if (l.isEmpty()) {
397 	    return null;
398 	} else {
399 	    return (MarshalledInstance[]) l.toArray(
400 		new MarshalledInstance[l.size()]);
401 	}
402     }
403 
404     /** 
405      * Set/update/clear the expiration warning listener.
406      * @param listener the new listener
407      * @param minWarning how long before the lease on the set expires should
408      *        the event be sent
409      * @param handback the new handback
410      * @return if <code>listener</code> is non-<code>null</code> return
411      * an <code>EventRegistration</code> otherwise return <code>null</code>
412      * @throws IOException if listener cannot be serialized 
413      */
414     EventRegistration setExpirationWarningListener(
415 	RemoteEventListener listener, 
416 	long                minWarning,
417 	MarshalledObject    handback)
418         throws IOException     					       
419     {
420 	synchronized (this){
421 	    this.minWarning = minWarning;
422 	}
423 	warningEventType.setListener(listener, handback);
424 
425 	final Object u = new WarningEventRegistration(this);
426 	store.update(u);
427 
428 	if (listener == null) 
429 	    return null;
430 	
431 	final SetProxy proxy = newSetProxy();
432 
433 	return new EventRegistration(
434 	    warningEventType.getEventID(),
435 	    proxy, 
436 	    proxy.getRenewalSetLease(), 
437 	    warningEventType.getLastSequenceNumber());
438     }
439 
440     /**
441      * Set/update/clear the renewal failure listener
442      * @param listener the new listener
443      * @param handback the new handback
444      * @return if <code>listener</code> is non-<code>null</code> return
445      * an <code>EventRegistration</code> otherwise return <code>null</code>
446      * @throws IOException if listener can not be serialized 
447      */
448     EventRegistration setRenewalFailureListener(
449 	RemoteEventListener listener, 
450 	MarshalledObject    handback)
451         throws IOException     					       
452     {
453 	failureEventType.setListener(listener, handback);
454 
455 	final Object u = new FailureEventRegistration(this);
456 	store.update(u);
457 
458 	if (listener == null) 
459 	    return null;
460 
461 	final SetProxy proxy = newSetProxy();
462 
463 	return new EventRegistration(
464 	    failureEventType.getEventID(),
465 	    proxy, 
466 	    proxy.getRenewalSetLease(), 
467 	    failureEventType.getLastSequenceNumber());
468     }
469 
470     /**
471      * Handle failures to renew a lease by removing the lease from the set
472      * and if needed schedule sending an event.
473      * @param clw the wrapped client lease for the lease that could not
474      *            be removed
475      */
476     void renewalFailure(ClientLeaseWrapper clw) {
477 	if (!removeInternal(clw)) {
478 	    // If we are here the lease must have already been
479 	    // removed by some other thread, don't bother logging or
480 	    // sending an event.
481 	    return;
482 	}
483 
484 	Object u;
485 	EventFactory factory;
486 	long seqNum;
487 
488 	try {
489 	    factory = clw.newFailureFactory(newSetProxy());
490 	    seqNum = failureEventType.sendEvent(factory);	
491 	} catch (IOException e) {
492 	    // We need to log the event even if we can't send it.
493 	    // Update the sequence number since an event has
494 	    // occurred. Note, we only get here if creating the
495 	    // factory fails (failureEventType.sendEvent() does not
496 	    // throw IOException) so we there is no danger of
497 	    // incrementing the sequence number twice
498 	    // $$$ is this the right thing to do?
499 	    seqNum = failureEventType.bumpSequenceNumber();
500 	}
501 
502 	u = new RenewalFailure(this, clw, seqNum);
503 	store.update(u);
504     }
505 
506     /**
507      * Send an expiration warning event for this set
508      */
509     void sendWarningEvent() {
510 	warningSeqNum = warningEventType.sendEvent(new WarningFactory(this));
511     }
512 
513     /**
514      * Nested class that implements <code>EventFactory</code> that
515      * generates <code>ExpirationWarningEvent</code>s.
516      */
517     private static class WarningFactory implements EventFactory {
518 	private static final long serialVersionUID = 1L; // Not Serializable
519 
520 	/** The source for the event */
521 	final private SetProxy proxy;
522 
523 	/**
524 	 * Create a new WarningFactory 
525 	 * @param set The set generating this event
526 	 */
527 	WarningFactory(LeaseSet set) {
528 	    proxy = set.newSetProxy();
529 	}
530 
531 	// Inherit java doc from super type
532 	public RemoteEvent createEvent(long             eventID, 
533 				       long             seqNum, 
534 				       MarshalledObject handback) 
535 	{
536 	    return new ExpirationWarningEvent(proxy, seqNum, handback);
537 	}	
538     }
539 
540     /**
541      * Create a new SetProxy for this set that has a lease with the
542      * current expiration     
543      */
544     private SetProxy newSetProxy() {
545 	return normServerBaseImpl.newSetProxy(this); 
546     }
547 
548 
549     /**
550      * Return true if there is a non-<code>null</code> listener registered
551      * for the expiration warning event.
552      * <p>
553      * Note, this method assumes the current thread owns the set's lock
554      */
555     boolean haveWarningRegistration() {
556 	return warningEventType.haveListener();
557     }
558 
559     /**
560      * Return the absolute time when a expiration warning should be sent.
561      */
562     long getWarningTime() {
563 	return expiration - minWarning;
564     }
565 
566     /**
567      * Log the renewal of a client lease.
568      * @param clw the wrapper for the client lease that was renewed
569      */
570     void logRenewal(ClientLeaseWrapper clw) {
571 	if (!leases.contains(clw)) {
572 	    // Some other thread must have removed this lease from
573 	    // the set after renewal, don't bother logging change
574 	    return;
575 	}
576 
577 	final Object u = new UpdateClientLease(this, clw);
578 	store.update(u);
579     }
580 
581     // Methods need to meet contract of LeasedResource	
582     // Inherit java doc from super type
583     public synchronized void setExpiration(long newExpiration) {
584 	expiration = newExpiration;
585 	// Update the 2nd copy
586 	expiration2.set(expiration);
587     }
588 
589     // Inherit java doc from super type
590     public synchronized long getExpiration() {
591 	return expiration;
592     }
593 
594     /**
595      * This method is used by the client lease renewal threads to make
596      * sure that the set associated with the lease they are renewing is 
597      * non-expired.  Note, the method check expiration2, not expiration
598      * so the renewal thread does not need to block on the set's lock.
599      * @param now the current time in milliseconds since the beginning of 
600      *            the epoch
601      */
602     boolean ensureCurrent(long now) {
603 	return expiration2.ensureCurrent(now);
604     }
605  
606     // Inherit java doc from super type
607     public Uuid getCookie() {
608 	return ID;
609     }
610 
611     /**
612      * Return the <code>Uuid</code> for this set.  */
613     Uuid getUuid() {
614 	return ID;
615     }
616 
617     /**
618      * If the passed registrationNumber number matches the 
619      * current registrationNumber for the passed event 
620      * clear the current registration and persist the change
621      */
622     void definiteException(EventType type, RemoteEvent ev,
623 			   long registrationNumber) 
624     {
625 	final boolean changed = 
626 	    type.clearListenerIfSequenceMatch(registrationNumber);
627 	if (changed) {
628 	    // Need to log the change
629 	    Object u;
630 	    if (ev instanceof ExpirationWarningEvent) {
631 		u = new WarningEventRegistration(LeaseSet.this);
632 	    } else {
633 		u = new FailureEventRegistration(LeaseSet.this);
634 	    }
635 	    store.update(u);
636 	}
637     }
638 
639     /**
640      * Returns whether to isolate renewal sets or batch leases across sets for
641      * all lease renewal sets associated with this set's service.
642      */
643     protected boolean isolateSets() {
644 	return normServerBaseImpl.isolateSets();
645     }
646 
647     /**
648      * Returns a string representation of this object.
649      */
650     public String toString() {
651 	return "LeaseSet" + ID;
652     }
653 
654     // Inner classes
655     /**
656      * Utility class that holds and guards the second copy of our expiration 
657      * time.
658      */
659     private static class ExpirationTime {
660 	private static final long serialVersionUID = 1L; // Not Serializable
661 
662 	/** 
663 	 * The expiration time in milliseconds since the beginning of the 
664 	 * epoch 
665 	 */
666 	private long expirationTime;
667 
668 	/** Simple constructor */
669 	private ExpirationTime(long initVal) {
670 	    expirationTime = initVal;
671 	}
672 
673 	/** Update the current expiration time */
674 	private synchronized void set(long newTime) {
675 	    expirationTime = newTime;
676 	}
677 
678 	/** 
679 	 * Return true if expiration time has not been reached 
680 	 * @param now the current time in milliseconds since the beginning of 
681 	 *            the epoch
682 	 */
683 	private synchronized boolean ensureCurrent(long now) {
684 	    return now <= expirationTime;
685 	}
686     }
687 	
688     // All the inner classes we use to log changes to a lease set
689 
690     /**
691      * Class used to log changes to the set expiration time.  
692      */
693     static class ChangeSetExpiration extends LeaseSetOperation {
694 	private static final long serialVersionUID = 1L;
695 
696 	/**
697 	 * Updated expiration time
698 	 * @serial
699 	 */
700 	private final long expiration;
701 
702 	/**
703 	 * Simple constructor
704 	 * @param set that changed
705 	 * @param expiration the new expiration time
706 	 */
707 	ChangeSetExpiration(LeaseSet set, long expiration) {
708 	    super(set.getUuid());
709 	    this.expiration = expiration;
710 	}
711 	
712 	// Inherit java doc from super type
713 	void apply(LeaseSet set) {
714 	    set.setExpiration(expiration);
715 	}
716     }
717 
718     /**
719      * Class used to log adding or updating a client lease to the set
720      */
721     private static class UpdateClientLease extends LeaseSetOperation {
722 	private static final long serialVersionUID = 1L;
723 
724 	/**
725 	 * Wrapped version of client lease
726 	 * @serial
727 	 */
728 	private final ClientLeaseWrapper clw;
729 
730 	/**
731 	 * Simple constructor
732 	 * @param set that changed
733 	 * @param clw Wrapped client lease
734 	 */
735 	private UpdateClientLease(LeaseSet set, ClientLeaseWrapper clw) {
736 	    super(set.getUuid());
737 	    this.clw = clw;
738 	}
739 
740 	// Inherit java doc from super type
741 	void apply(LeaseSet set) {
742 	    set.replace(clw);
743 	}
744     }
745 
746     /**
747      * Class used to log the removal of a client lease from the set
748      */
749     private static class RemoveClientLease extends LeaseSetOperation {
750 	private static final long serialVersionUID = 1L;
751 
752 	/**
753 	 * Client lease to be removed
754 	 * @serial
755 	 */
756 	private final ClientLeaseWrapper clw;
757 
758 	/**
759 	 * Simple constructor
760 	 * @param set that changed
761 	 * @param clw Wrapped client lease
762 	 */
763 	private RemoveClientLease(LeaseSet set, ClientLeaseWrapper clw) {
764 	    super(set.getUuid());
765 	    this.clw = clw;
766 	}
767 
768 	
769 	// Inherit java doc from super type
770 	void apply(LeaseSet set) {
771 	    set.leases.remove(clw);
772 	}
773     }
774 
775     /**
776      * Class used to log a renewal failure
777      */
778     private static class RenewalFailure extends RemoveClientLease {
779 	private static final long serialVersionUID = 1L;
780 
781 	/**
782 	 * Event ID of the corresponding renewal failure event (if any)
783 	 * @serial
784 	 */
785 	private long evID;
786 
787 	/**
788 	 * Simple constructor
789 	 * @param set that changed
790 	 * @param clw Wrapped client lease
791 	 * @param evID event ID of the renewal event that was sent
792 	 *             to mark this renewal failure
793 	 */
794 	private RenewalFailure(LeaseSet set, ClientLeaseWrapper clw,
795 			       long evID)
796 	{
797 	    super(set, clw);
798 	    this.evID = evID;
799 	}
800 	
801 	// Inherit java doc from super type
802 	void apply(LeaseSet set) {
803 	    super.apply(set);
804 	    set.failureEventType.setLastSequenceNumber(evID);
805 	}
806     }
807 
808     /**
809      * Class used to log changes to warning event registrations
810      */
811     private static class WarningEventRegistration extends LeaseSetOperation {
812 	private static final long serialVersionUID = 1L;
813 
814 	/**
815 	 * Warning time associated with warning event registration
816 	 * @serial
817 	 */
818 	private final long warningTime;
819 
820 	/**
821 	 * EventType object that resulted from registration change
822 	 * @serial
823 	 */
824 	private final EventType registration;
825 
826 	/**
827 	 * Simple constructor
828 	 * @param set that changed
829 	 */
830 	private WarningEventRegistration(LeaseSet set) {
831 	    super(set.getUuid());
832 	    synchronized (set){
833 		warningTime = set.minWarning;
834 		registration = set.warningEventType;
835 	    }
836 	}
837 
838 	
839 	// Inherit java doc from super type
840 	void apply(LeaseSet set) throws StoreException {
841 	    synchronized (set){
842 		set.minWarning = warningTime;
843 		set.warningEventType = registration;
844 	    }
845 	}
846     }
847 
848     /**
849      * Class used to log changes to failure event registrations
850      */
851     private static class FailureEventRegistration extends LeaseSetOperation {
852 	private static final long serialVersionUID = 1L;
853 
854 	/**
855 	 * EventType object that resulted from registration change
856 	 * @serial
857 	 */
858 	private final EventType registration;
859 
860 	/**
861 	 * Simple constructor
862 	 * @param set that changed
863 	 */
864 	private FailureEventRegistration(LeaseSet set) {
865 	    super(set.getUuid());
866 	    registration = set.failureEventType;
867 	}
868 
869 	
870 	// Inherit java doc from super type
871 	void apply(LeaseSet set) throws StoreException {
872 	    set.failureEventType = registration;
873 	}
874     }
875 }