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    }