001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.replication.protocol;
028
029 import java.io.Serializable;
030 import java.io.UnsupportedEncodingException;
031 import java.util.zip.DataFormatException;
032 import java.util.ArrayList;
033 import java.util.HashMap;
034 import java.util.Iterator;
035 import java.util.Set;
036
037 import org.opends.server.replication.common.ServerState;
038 import org.opends.server.protocols.asn1.ASN1OctetString;
039 import org.opends.server.protocols.asn1.ASN1Sequence;
040 import org.opends.server.protocols.asn1.ASN1Element;
041 import org.opends.server.replication.common.ChangeNumber;
042
043 /**
044 * This message is part of the replication protocol.
045 * RS1 sends a MonitorRequestMessage to RS2 to requests its monitoring
046 * informations.
047 * When RS2 receives a MonitorRequestMessage from RS1, RS2 responds with a
048 * MonitorMessage.
049 */
050 public class MonitorMessage extends RoutableMessage implements
051 Serializable
052 {
053
054 private static final long serialVersionUID = -1900670921496804942L;
055
056 /**
057 * Data structure to manage the state and the approximation
058 * of the data of the first missing change for each LDAP server
059 * connected to a Replication Server.
060 */
061 class ServerData
062 {
063 ServerState state;
064 Long approxFirstMissingDate;
065 }
066
067 /**
068 * Data structure to manage the state of this replication server
069 * and the state informations for the servers connected to it.
070 *
071 */
072 class SubTopoMonitorData
073 {
074 // This replication server DbState
075 ServerState replServerDbState;
076 // The data related to the LDAP servers connected to this RS
077 HashMap<Short, ServerData> ldapStates =
078 new HashMap<Short, ServerData>();
079 // The data related to the RS servers connected to this RS
080 HashMap<Short, ServerData> rsStates =
081 new HashMap<Short, ServerData>();
082 }
083
084 SubTopoMonitorData data = new SubTopoMonitorData();;
085
086 /**
087 * Creates a new EntryMessage.
088 *
089 * @param sender The sender of this message.
090 * @param destination The destination of this message.
091 */
092 public MonitorMessage(short sender, short destination)
093 {
094 super(sender, destination);
095 }
096
097 /**
098 * Sets the state of the replication server.
099 * @param state The state.
100 */
101 public void setReplServerDbState(ServerState state)
102 {
103 data.replServerDbState = state;
104 }
105
106 /**
107 * Sets the informations of an LDAP server.
108 * @param serverId The serverID.
109 * @param state The server state.
110 * @param approxFirstMissingDate The approximation of the date
111 * of the older missing change. null when none.
112 * @param isLDAP Specifies whether the server is a LS or a RS
113 */
114 public void setServerState(short serverId, ServerState state,
115 Long approxFirstMissingDate, boolean isLDAP)
116 {
117 if (data.ldapStates == null)
118 {
119 data.ldapStates = new HashMap<Short, ServerData>();
120 }
121 if (data.rsStates == null)
122 {
123 data.rsStates = new HashMap<Short, ServerData>();
124 }
125 ServerData sd = new ServerData();
126 sd.state = state;
127 sd.approxFirstMissingDate = approxFirstMissingDate;
128 if (isLDAP)
129 data.ldapStates.put(serverId, sd);
130 else
131 data.rsStates.put(serverId, sd);
132 }
133
134 /**
135 * Get the server state for the LDAP server with the provided serverId.
136 * @param serverId The provided serverId.
137 * @return The state.
138 */
139 public ServerState getLDAPServerState(short serverId)
140 {
141 return data.ldapStates.get(serverId).state;
142 }
143
144 /**
145 * Get the server state for the RS server with the provided serverId.
146 * @param serverId The provided serverId.
147 * @return The state.
148 */
149 public ServerState getRSServerState(short serverId)
150 {
151 return data.rsStates.get(serverId).state;
152 }
153
154
155 /**
156 * Get the approximation of the date of the older missing change for the
157 * LDAP Server with the provided server Id.
158 * @param serverId The provided serverId.
159 * @return The approximated state.
160 */
161 public Long getLDAPApproxFirstMissingDate(short serverId)
162 {
163 return data.ldapStates.get(serverId).approxFirstMissingDate;
164 }
165
166 /**
167 * Get the approximation of the date of the older missing change for the
168 * RS Server with the provided server Id.
169 * @param serverId The provided serverId.
170 * @return The approximated state.
171 */
172 public Long getRSApproxFirstMissingDate(short serverId)
173 {
174 return data.rsStates.get(serverId).approxFirstMissingDate;
175 }
176
177 /**
178 * Creates a new EntryMessage from its encoded form.
179 *
180 * @param in The byte array containing the encoded form of the message.
181 * @throws DataFormatException If the byte array does not contain a valid
182 * encoded form of the ServerStartMessage.
183 */
184 public MonitorMessage(byte[] in) throws DataFormatException
185 {
186 try
187 {
188 /* first byte is the type */
189 if (in[0] != MSG_TYPE_REPL_SERVER_MONITOR)
190 throw new DataFormatException("input is not a valid " +
191 this.getClass().getCanonicalName());
192 int pos = 1;
193
194 // sender
195 int length = getNextLength(in, pos);
196 String senderIDString = new String(in, pos, length, "UTF-8");
197 this.senderID = Short.valueOf(senderIDString);
198 pos += length +1;
199
200 // destination
201 length = getNextLength(in, pos);
202 String destinationString = new String(in, pos, length, "UTF-8");
203 this.destination = Short.valueOf(destinationString);
204 pos += length +1;
205
206 /* Read the states : all the remaining bytes but the terminating 0 */
207 byte[] encodedS = new byte[in.length-pos-1];
208 int i =0;
209 while (pos<in.length-1)
210 {
211 encodedS[i++] = in[pos++];
212 }
213
214
215 try
216 {
217 ASN1Sequence s0 = ASN1Sequence.decodeAsSequence(encodedS);
218 // loop on the servers
219 for (ASN1Element el0 : s0.elements())
220 {
221 ServerState newState = new ServerState();
222 short serverId = 0;
223 Long outime = (long)0;
224 boolean isLDAPServer = false;
225 ASN1Sequence s1 = el0.decodeAsSequence();
226
227 // loop on the list of CN of the state
228 for (ASN1Element el1 : s1.elements())
229 {
230 ASN1OctetString o = el1.decodeAsOctetString();
231 String s = o.stringValue();
232 ChangeNumber cn = new ChangeNumber(s);
233 if ((data.replServerDbState != null) && (serverId == 0))
234 {
235 // we are on the first CN that is a fake CN to store the serverId
236 // and the older update time
237 serverId = cn.getServerId();
238 outime = cn.getTime();
239 isLDAPServer = (cn.getSeqnum()>0);
240 }
241 else
242 {
243 // we are on a normal CN
244 newState.update(cn);
245 }
246 }
247
248 if (data.replServerDbState == null)
249 {
250 // the first state is the replication state
251 data.replServerDbState = newState;
252 }
253 else
254 {
255 // the next states are the server states
256 ServerData sd = new ServerData();
257 sd.state = newState;
258 sd.approxFirstMissingDate = outime;
259 if (isLDAPServer)
260 data.ldapStates.put(serverId, sd);
261 else
262 data.rsStates.put(serverId, sd);
263 }
264 }
265 } catch(Exception e)
266 {
267
268 }
269 }
270 catch (UnsupportedEncodingException e)
271 {
272 throw new DataFormatException("UTF-8 is not supported by this jvm.");
273 }
274 }
275
276 /**
277 * {@inheritDoc}
278 */
279 @Override
280 public byte[] getBytes()
281 {
282 try
283 {
284 byte[] senderBytes = String.valueOf(senderID).getBytes("UTF-8");
285 byte[] destinationBytes = String.valueOf(destination).getBytes("UTF-8");
286
287 int length = 1 + senderBytes.length +
288 1 + destinationBytes.length;
289
290 ASN1Sequence stateElementSequence = new ASN1Sequence();
291 ArrayList<ASN1Element> stateElementList = new ArrayList<ASN1Element>();
292
293 /**
294 * First loop computes the length
295 */
296
297 /* Put the serverStates ... */
298 stateElementSequence = new ASN1Sequence();
299 stateElementList = new ArrayList<ASN1Element>();
300
301 /* first put the Replication Server state */
302 ArrayList<ASN1OctetString> cnOctetList =
303 data.replServerDbState.toASN1ArrayList();
304 ArrayList<ASN1Element> cnElementList = new ArrayList<ASN1Element>();
305 for (ASN1OctetString soci : cnOctetList)
306 {
307 cnElementList.add(soci);
308 }
309 ASN1Sequence cnSequence = new ASN1Sequence(cnElementList);
310 stateElementList.add(cnSequence);
311
312 // then the LDAP server data
313 Set<Short> servers = data.ldapStates.keySet();
314 for (Short sid : servers)
315 {
316 // State
317 ServerState statei = data.ldapStates.get(sid).state;
318 // First missing date
319 Long outime = data.ldapStates.get(sid).approxFirstMissingDate;
320
321 // retrieves the change numbers as an arrayList of ANSN1OctetString
322 cnOctetList = statei.toASN1ArrayList();
323 cnElementList = new ArrayList<ASN1Element>();
324
325 // a fake changenumber helps storing the LDAP server ID
326 // and the olderupdatetime
327 ChangeNumber cn = new ChangeNumber(outime,0,sid);
328 cnElementList.add(new ASN1OctetString(cn.toString()));
329
330 // the changenumbers
331 for (ASN1OctetString soci : cnOctetList)
332 {
333 cnElementList.add(soci);
334 }
335
336 cnSequence = new ASN1Sequence(cnElementList);
337 stateElementList.add(cnSequence);
338 }
339
340 // then the rs server data
341 servers = data.rsStates.keySet();
342 for (Short sid : servers)
343 {
344 // State
345 ServerState statei = data.rsStates.get(sid).state;
346 // First missing date
347 Long outime = data.rsStates.get(sid).approxFirstMissingDate;
348
349 // retrieves the change numbers as an arrayList of ANSN1OctetString
350 cnOctetList = statei.toASN1ArrayList();
351 cnElementList = new ArrayList<ASN1Element>();
352
353 // a fake changenumber helps storing the LDAP server ID
354 // and the olderupdatetime
355 ChangeNumber cn = new ChangeNumber(outime,0,sid);
356 cnElementList.add(new ASN1OctetString(cn.toString()));
357
358 // the changenumbers
359 for (ASN1OctetString soci : cnOctetList)
360 {
361 cnElementList.add(soci);
362 }
363
364 cnSequence = new ASN1Sequence(cnElementList);
365 stateElementList.add(cnSequence);
366 }
367
368 stateElementSequence.setElements(stateElementList);
369 int seqLen = stateElementSequence.encode().length;
370
371 //
372 length += seqLen;
373 length += 2;
374
375 // Allocate the array sized from the computed length
376 byte[] resultByteArray = new byte[length];
377
378 /**
379 * Second loop really builds the array
380 */
381
382 /* put the type of the operation */
383 resultByteArray[0] = MSG_TYPE_REPL_SERVER_MONITOR;
384 int pos = 1;
385
386 pos = addByteArray(senderBytes, resultByteArray, pos);
387 pos = addByteArray(destinationBytes, resultByteArray, pos);
388
389 /* Put the serverStates ... */
390 stateElementSequence = new ASN1Sequence();
391 stateElementList = new ArrayList<ASN1Element>();
392
393 /* first put the Replication Server state */
394 cnOctetList =
395 data.replServerDbState.toASN1ArrayList();
396 cnElementList = new ArrayList<ASN1Element>();
397 for (ASN1OctetString soci : cnOctetList)
398 {
399 cnElementList.add(soci);
400 }
401 cnSequence = new ASN1Sequence(cnElementList);
402 stateElementList.add(cnSequence);
403
404 // then the LDAP server datas
405 servers = data.ldapStates.keySet();
406 for (Short sid : servers)
407 {
408 ServerState statei = data.ldapStates.get(sid).state;
409 Long outime = data.ldapStates.get(sid).approxFirstMissingDate;
410
411 // retrieves the change numbers as an arrayList of ANSN1OctetString
412 cnOctetList = statei.toASN1ArrayList();
413 cnElementList = new ArrayList<ASN1Element>();
414
415 // a fake changenumber helps storing the LDAP server ID
416 ChangeNumber cn = new ChangeNumber(outime,1,sid);
417 cnElementList.add(new ASN1OctetString(cn.toString()));
418
419 // the changenumbers that make the state
420 for (ASN1OctetString soci : cnOctetList)
421 {
422 cnElementList.add(soci);
423 }
424
425 cnSequence = new ASN1Sequence(cnElementList);
426 stateElementList.add(cnSequence);
427 }
428
429 // then the RS server datas
430 servers = data.rsStates.keySet();
431 for (Short sid : servers)
432 {
433 ServerState statei = data.rsStates.get(sid).state;
434 Long outime = data.rsStates.get(sid).approxFirstMissingDate;
435
436 // retrieves the change numbers as an arrayList of ANSN1OctetString
437 cnOctetList = statei.toASN1ArrayList();
438 cnElementList = new ArrayList<ASN1Element>();
439
440 // a fake changenumber helps storing the LDAP server ID
441 ChangeNumber cn = new ChangeNumber(outime,0,sid);
442 cnElementList.add(new ASN1OctetString(cn.toString()));
443
444 // the changenumbers that make the state
445 for (ASN1OctetString soci : cnOctetList)
446 {
447 cnElementList.add(soci);
448 }
449
450 cnSequence = new ASN1Sequence(cnElementList);
451 stateElementList.add(cnSequence);
452 }
453
454
455 stateElementSequence.setElements(stateElementList);
456 pos = addByteArray(stateElementSequence.encode(), resultByteArray, pos);
457
458 return resultByteArray;
459 }
460 catch (UnsupportedEncodingException e)
461 {
462 return null;
463 }
464 }
465
466 /**
467 * Get the state of the replication server that sent this message.
468 * @return The state.
469 */
470 public ServerState getReplServerDbState()
471 {
472 return data.replServerDbState;
473 }
474
475 /**
476 * Returns an iterator on the serverId of the connected LDAP servers.
477 * @return The iterator.
478 */
479 public Iterator<Short> ldapIterator()
480 {
481 return data.ldapStates.keySet().iterator();
482 }
483
484 /**
485 * Returns an iterator on the serverId of the connected RS servers.
486 * @return The iterator.
487 */
488 public Iterator<Short> rsIterator()
489 {
490 return data.rsStates.keySet().iterator();
491 }
492
493 /**
494 * {@inheritDoc}
495 */
496 @Override
497 public String toString()
498 {
499 String stateS = "\nRState:[";
500 stateS += data.replServerDbState.toString();
501 stateS += "]";
502
503 stateS += "\nLDAPStates:[";
504 for (Short sid : data.ldapStates.keySet())
505 {
506 ServerData sd = data.ldapStates.get(sid);
507 stateS +=
508 "\n[LSstate("+ sid + ")=" +
509 sd.state.toString() + "]" +
510 " afmd=" + sd.approxFirstMissingDate + "]";
511 }
512
513 stateS += "\nRSStates:[";
514 for (Short sid : data.rsStates.keySet())
515 {
516 ServerData sd = data.rsStates.get(sid);
517 stateS +=
518 "\n[RSState("+ sid + ")=" +
519 sd.state.toString() + "]" +
520 " afmd=" + sd.approxFirstMissingDate + "]";
521 }
522 String me = this.getClass().getCanonicalName() +
523 "[ sender=" + this.senderID +
524 " destination=" + this.destination +
525 " data=[" + stateS + "]" +
526 "]";
527 return me;
528 }
529 }