1 /*
2 * Copyright (c) 1998-2004 The Jgroup Team.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 */
18
19 package jgroup.arm;
20
21 import java.io.IOException;
22 import java.io.ObjectInput;
23 import java.io.ObjectOutput;
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.util.Timer;
27 import java.util.TimerTask;
28
29 import jgroup.core.arm.ARMEvent;
30 import jgroup.core.arm.DistributionScheme;
31 import jgroup.relacs.config.AppConfig;
32 import jgroup.relacs.types.ViewImpl;
33
34 import org.apache.log4j.Logger;
35
36 /**
37 * @author Hein Meling
38 * @since Jgroup 2.1
39 */
40 public class ReplicaPingEvent
41 implements ARMEvent
42 {
43
44 private static final long serialVersionUID = -6041617835370010146L;
45
46 ////////////////////////////////////////////////////////////////////////////////////////////
47 // Logger
48 ////////////////////////////////////////////////////////////////////////////////////////////
49
50 /** Obtain logger for this class */
51 private static final Logger log = Logger.getLogger(ReplicaPingEvent.class);
52
53
54 ////////////////////////////////////////////////////////////////////////////////////////////
55 // Constants
56 ////////////////////////////////////////////////////////////////////////////////////////////
57
58 /** The smallest allowed ping rate */
59 private static final int MIN_PING_RATE = 1000;
60
61 /** The default ping period for lease renewal */
62 private static final int DEFAULT_PING_RATE = 2000;
63
64
65 ////////////////////////////////////////////////////////////////////////////////////////////
66 // Fields
67 ////////////////////////////////////////////////////////////////////////////////////////////
68
69 /** The group identifier for issuer of this ping event */
70 private int gid;
71
72 /** The period to wait before alarming ARM */
73 private int suspectPeriod;
74
75
76 ////////////////////////////////////////////////////////////////////////////////////////////
77 // Static and transient fields
78 ////////////////////////////////////////////////////////////////////////////////////////////
79
80 /** Map of renew timers (groupid -> renew timer for group) */
81 private static transient final Map<Integer,Timer> renewTimers =
82 new HashMap<Integer,Timer>(5);
83
84 /** Access point to the replication manager for external events */
85 private transient DistributionScheme distScheme;
86
87 /**
88 * The actual ping rate associated with this PingEvent object.
89 * This is the period between each renewal invocations performed
90 * by the <code>RecoveryLayer</code>.
91 *
92 * Note that since this field is transient, it will not be accessible
93 * to JVMs creating this object using the no-argument constructor.
94 * (deserialization).
95 */
96 private transient int pingRate;
97
98
99 ////////////////////////////////////////////////////////////////////////////////////////////
100 // Constructors
101 ////////////////////////////////////////////////////////////////////////////////////////////
102
103 /**
104 * Default constructor for externalization
105 */
106 public ReplicaPingEvent() { }
107
108 /**
109 * Private constructor to prevent external construction.
110 *
111 * @param gid
112 * @param pingRate
113 */
114 private ReplicaPingEvent(int gid, int pingRate)
115 {
116 this.gid = gid;
117 this.pingRate = pingRate;
118 if (pingRate < MIN_PING_RATE)
119 throw new IllegalArgumentException("Ping rate must be greater than " + MIN_PING_RATE);
120 suspectPeriod = 2*pingRate;
121 }
122
123 /**
124 * @param gid the group identifier of the group renewing its lease
125 * @param groupSize the current size of the group
126 */
127 public static ReplicaPingEvent getNewPingEvent(int gid, int groupSize)
128 {
129 AppConfig app = AppConfig.getApplication(gid);
130 boolean groupFailureSupport = app.getBooleanParam("Recovery.GroupFailureSupport");
131 if (groupFailureSupport) {
132 int basePingRate = app.getIntParam("Recovery.RenewalRate", DEFAULT_PING_RATE);
133 int pingRate = (int) (Math.pow(basePingRate/1000, groupSize)*1000);
134 return new ReplicaPingEvent(gid, pingRate);
135 } else {
136 return null;
137 }
138 }
139
140 ////////////////////////////////////////////////////////////////////////////////////////////
141 // Access methods
142 ////////////////////////////////////////////////////////////////////////////////////////////
143
144 /**
145 * Get the ping rate associated with this object. <p>
146 *
147 * Note that this can only be called on the object created by
148 * the argument constructor, since the <code>pingRate</code> field
149 * is not serialized.
150 */
151 public int getPingRate()
152 {
153 return pingRate;
154 }
155
156
157 ////////////////////////////////////////////////////////////////////////////////////////////
158 // ARMEvent interface methods
159 ////////////////////////////////////////////////////////////////////////////////////////////
160
161 /* (non-Javadoc)
162 * @see jgroup.core.arm.ARMEvent#handle()
163 */
164 public void handle(DistributionScheme distScheme)
165 {
166 this.distScheme = distScheme;
167 synchronized (renewTimers) {
168 Timer renewTimer = (Timer) renewTimers.get(gid);
169 if (renewTimer != null) {
170 renewTimer.cancel();
171 }
172 renewTimer = new Timer("RenewTimer-" + gid, true);
173 renewTimers.put(gid, renewTimer);
174 if (log.isDebugEnabled()) {
175 log.debug("Lease expires @ " + (System.currentTimeMillis() + suspectPeriod)
176 + ", group: " + gid);
177 }
178 renewTimer.schedule(new RenewTask(), suspectPeriod);
179 }
180 }
181
182
183 /* (non-Javadoc)
184 * @see jgroup.core.arm.ARMEvent#getGroupId()
185 */
186 public int getGroupId()
187 {
188 return gid;
189 }
190
191
192 /* (non-Javadoc)
193 * @see jgroup.core.arm.ARMEvent#getObject()
194 */
195 public Object getObject()
196 {
197 // Currently we ignore these events.
198 return null;
199 }
200
201
202 /**
203 * This timer task is run only when a renewal does not occur.
204 */
205 private class RenewTask
206 extends TimerTask
207 {
208
209 /* (non-Javadoc)
210 * @see java.util.TimerTask#run()
211 */
212 public void run()
213 {
214 if (log.isDebugEnabled())
215 log.debug("Lease expired @ " + System.currentTimeMillis());
216 synchronized (renewTimers) {
217 renewTimers.remove(gid);
218 }
219 distScheme.viewChangeEvent(new ViewImpl(gid));
220 }
221
222 } // END RenewTask
223
224
225 public String toString()
226 {
227 return "ReplicaPingEvent: gid=" + gid + ", suspect_period=" + suspectPeriod;
228 }
229
230
231 ////////////////////////////////////////////////////////////////////////////////////////////
232 // Methods from the Externalizable interface (as required by the ARMEvent interface)
233 ////////////////////////////////////////////////////////////////////////////////////////////
234
235 /* (non-Javadoc)
236 * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
237 */
238 public void readExternal(ObjectInput in)
239 throws IOException, ClassNotFoundException
240 {
241 gid = in.readInt();
242 suspectPeriod = in.readInt();
243 }
244
245 /* (non-Javadoc)
246 * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
247 */
248 public void writeExternal(ObjectOutput out)
249 throws IOException
250 {
251 out.writeInt(gid);
252 out.writeInt(suspectPeriod);
253 }
254
255 } // END ReplicaPingEvent