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 }