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.backends.jeb;
028    
029    
030    
031    import java.util.ArrayList;
032    import java.util.LinkedHashMap;
033    import java.util.LinkedHashSet;
034    import java.util.Map;
035    import java.util.concurrent.ConcurrentHashMap;
036    import java.util.concurrent.atomic.AtomicInteger;
037    
038    import com.sleepycat.je.Cursor;
039    import com.sleepycat.je.Database;
040    import com.sleepycat.je.DatabaseConfig;
041    import com.sleepycat.je.DatabaseEntry;
042    import com.sleepycat.je.DatabaseException;
043    import com.sleepycat.je.DeadlockException;
044    import com.sleepycat.je.Environment;
045    import com.sleepycat.je.LockMode;
046    import com.sleepycat.je.OperationStatus;
047    
048    import org.opends.messages.Message;
049    import org.opends.server.api.CompressedSchema;
050    import org.opends.server.core.DirectoryServer;
051    import org.opends.server.loggers.debug.DebugTracer;
052    import org.opends.server.protocols.asn1.ASN1Element;
053    import org.opends.server.protocols.asn1.ASN1Exception;
054    import org.opends.server.protocols.asn1.ASN1OctetString;
055    import org.opends.server.protocols.asn1.ASN1Sequence;
056    import org.opends.server.types.Attribute;
057    import org.opends.server.types.AttributeType;
058    import org.opends.server.types.AttributeValue;
059    import org.opends.server.types.ByteArray;
060    import org.opends.server.types.DebugLogLevel;
061    import org.opends.server.types.DirectoryException;
062    import org.opends.server.types.ObjectClass;
063    
064    import static org.opends.server.config.ConfigConstants.*;
065    import static org.opends.server.loggers.debug.DebugLogger.*;
066    import static org.opends.messages.JebMessages.*;
067    import static org.opends.server.util.StaticUtils.*;
068    
069    
070    
071    /**
072     * This class provides a compressed schema implementation whose definitions are
073     * stored in a Berkeley DB JE database.
074     */
075    public final class JECompressedSchema
076           extends CompressedSchema
077    {
078      /**
079       * The tracer object for the debug logger.
080       */
081      private static final DebugTracer TRACER = getTracer();
082    
083    
084    
085      /**
086       * The name of the database used to store compressed attribute description
087       * definitions.
088       */
089      public static final String DB_NAME_AD = "compressed_attributes";
090    
091    
092    
093      /**
094       * The name of the database used to store compressed object class set
095       * definitions.
096       */
097      public static final String DB_NAME_OC = "compressed_object_classes";
098    
099    
100    
101      // The counter used for attribute descriptions.
102      private AtomicInteger adCounter;
103    
104      // The counter used for object class sets.
105      private AtomicInteger ocCounter;
106    
107      // The map between encoded representations and attribute types.
108      private ConcurrentHashMap<ByteArray,AttributeType> atDecodeMap;
109    
110      // The map between encoded representations and attribute options.
111      private ConcurrentHashMap<ByteArray,LinkedHashSet<String>> aoDecodeMap;
112    
113      // The map between encoded representations and object class sets.
114      private ConcurrentHashMap<ByteArray,Map<ObjectClass,String>> ocDecodeMap;
115    
116      // The map between attribute descriptions and their encoded
117      // representations.
118      private ConcurrentHashMap<AttributeType,
119                   ConcurrentHashMap<LinkedHashSet<String>,ByteArray>> adEncodeMap;
120    
121      // The map between object class sets and encoded representations.
122      private ConcurrentHashMap<Map<ObjectClass,String>,ByteArray> ocEncodeMap;
123    
124      // The compressed attribute description schema database.
125      private Database adDatabase;
126    
127      // The compresesd object class set schema database.
128      private Database ocDatabase;
129    
130      // The environment in which the databases are held.
131      private Environment environment;
132    
133    
134    
135      /**
136       * Creates a new instance of this JE compressed schema manager.
137       *
138       * @param  environment  A reference to the database environment in which the
139       *                      databases will be held.
140       *
141       * @throws  DatabaseException  If a problem occurs while loading the
142       *                             compressed schema definitions from the
143       *                             database.
144       */
145      public JECompressedSchema(Environment environment)
146             throws DatabaseException
147      {
148        this.environment = environment;
149    
150        atDecodeMap = new ConcurrentHashMap<ByteArray,AttributeType>();
151        aoDecodeMap = new ConcurrentHashMap<ByteArray,LinkedHashSet<String>>();
152        ocDecodeMap = new ConcurrentHashMap<ByteArray,Map<ObjectClass,String>>();
153        adEncodeMap =
154             new ConcurrentHashMap<AttributeType,
155                      ConcurrentHashMap<LinkedHashSet<String>,ByteArray>>();
156        ocEncodeMap = new ConcurrentHashMap<Map<ObjectClass,String>,ByteArray>();
157    
158        adCounter = new AtomicInteger(1);
159        ocCounter = new AtomicInteger(1);
160    
161        load();
162      }
163    
164    
165    
166      /**
167       * Loads the compressed schema information from the database.
168       *
169       * @throws  DatabaseException  If a problem occurs while loading the
170       *                             definitions from the database.
171       */
172      private void load()
173              throws DatabaseException
174      {
175        DatabaseConfig dbConfig = new DatabaseConfig();
176    
177        if(environment.getConfig().getReadOnly())
178        {
179          dbConfig.setReadOnly(true);
180          dbConfig.setAllowCreate(false);
181          dbConfig.setTransactional(false);
182        }
183        else if(!environment.getConfig().getTransactional())
184        {
185          dbConfig.setAllowCreate(true);
186          dbConfig.setTransactional(false);
187          dbConfig.setDeferredWrite(true);
188        }
189        else
190        {
191          dbConfig.setAllowCreate(true);
192          dbConfig.setTransactional(true);
193        }
194    
195        adDatabase = environment.openDatabase(null, DB_NAME_AD, dbConfig);
196        ocDatabase = environment.openDatabase(null, DB_NAME_OC, dbConfig);
197    
198        // Cursor through the object class database and load the object class set
199        // definitions.  At the same time, figure out the highest token value and
200        // initialize the object class counter to one greater than that.
201        Cursor ocCursor = ocDatabase.openCursor(null, null);
202        int highestToken = 0;
203    
204        try
205        {
206          DatabaseEntry keyEntry   = new DatabaseEntry();
207          DatabaseEntry valueEntry = new DatabaseEntry();
208          OperationStatus status = ocCursor.getFirst(keyEntry, valueEntry,
209                                                     LockMode.READ_UNCOMMITTED);
210          while (status == OperationStatus.SUCCESS)
211          {
212            ByteArray token = new ByteArray(keyEntry.getData());
213            highestToken = Math.max(highestToken, decodeInt(token.array()));
214    
215            ArrayList<ASN1Element> elements =
216                 ASN1Sequence.decodeAsSequence(valueEntry.getData()).elements();
217            LinkedHashMap<ObjectClass,String> ocMap =
218                 new LinkedHashMap<ObjectClass,String>(elements.size());
219            for (int i=0; i < elements.size(); i++)
220            {
221              ASN1OctetString os = elements.get(i).decodeAsOctetString();
222              String ocName = os.stringValue();
223              String lowerName = toLowerCase(ocName);
224              ObjectClass oc = DirectoryServer.getObjectClass(lowerName, true);
225              ocMap.put(oc, ocName);
226            }
227    
228            ocEncodeMap.put(ocMap, token);
229            ocDecodeMap.put(token, ocMap);
230    
231            status = ocCursor.getNext(keyEntry, valueEntry,
232                                      LockMode.READ_UNCOMMITTED);
233          }
234        }
235        catch (ASN1Exception ae)
236        {
237          if (debugEnabled())
238          {
239            TRACER.debugCaught(DebugLogLevel.ERROR, ae);
240          }
241    
242          Message m =
243               ERR_JEB_COMPSCHEMA_CANNOT_DECODE_OC_TOKEN.get(ae.getMessage());
244          throw new DatabaseException(m.toString(), ae);
245        }
246        finally
247        {
248          ocCursor.close();
249        }
250    
251        ocCounter.set(highestToken+1);
252    
253    
254        // Cursor through the attribute description database and load the attribute
255        // set definitions.
256        Cursor adCursor = adDatabase.openCursor(null, null);
257        highestToken = 0;
258    
259        try
260        {
261          DatabaseEntry keyEntry   = new DatabaseEntry();
262          DatabaseEntry valueEntry = new DatabaseEntry();
263          OperationStatus status = adCursor.getFirst(keyEntry, valueEntry,
264                                                     LockMode.READ_UNCOMMITTED);
265          while (status == OperationStatus.SUCCESS)
266          {
267            ByteArray token = new ByteArray(keyEntry.getData());
268            highestToken = Math.max(highestToken, decodeInt(token.array()));
269    
270            ArrayList<ASN1Element> elements =
271                 ASN1Sequence.decodeAsSequence(valueEntry.getData()).elements();
272    
273            ASN1OctetString os = elements.get(0).decodeAsOctetString();
274            String attrName = os.stringValue();
275            String lowerName = toLowerCase(attrName);
276            AttributeType attrType =
277                 DirectoryServer.getAttributeType(lowerName, true);
278    
279            LinkedHashSet<String> options =
280                 new LinkedHashSet<String>(elements.size()-1);
281            for (int i=1; i < elements.size(); i++)
282            {
283              os = elements.get(i).decodeAsOctetString();
284              options.add(os.stringValue());
285            }
286    
287            atDecodeMap.put(token, attrType);
288            aoDecodeMap.put(token, options);
289    
290            ConcurrentHashMap<LinkedHashSet<String>,ByteArray> map =
291                 adEncodeMap.get(attrType);
292            if (map == null)
293            {
294              map = new ConcurrentHashMap<LinkedHashSet<String>,ByteArray>(1);
295              map.put(options, token);
296              adEncodeMap.put(attrType, map);
297            }
298            else
299            {
300              map.put(options, token);
301            }
302    
303            status = adCursor.getNext(keyEntry, valueEntry,
304                                      LockMode.READ_UNCOMMITTED);
305          }
306        }
307        catch (ASN1Exception ae)
308        {
309          if (debugEnabled())
310          {
311            TRACER.debugCaught(DebugLogLevel.ERROR, ae);
312          }
313    
314          Message m =
315               ERR_JEB_COMPSCHEMA_CANNOT_DECODE_AD_TOKEN.get(ae.getMessage());
316          throw new DatabaseException(m.toString(), ae);
317        }
318        finally
319        {
320          adCursor.close();
321        }
322    
323        adCounter.set(highestToken+1);
324      }
325    
326    
327    
328      /**
329       * Closes the databases and releases any resources held by this compressed
330       * schema manager.
331       */
332      public void close()
333      {
334        try
335        {
336          adDatabase.sync();
337        } catch (Exception e) {}
338    
339        try
340        {
341          adDatabase.close();
342        } catch (Exception e) {}
343    
344        try
345        {
346          ocDatabase.sync();
347        } catch (Exception e) {}
348    
349        try
350        {
351          ocDatabase.close();
352        } catch (Exception e) {}
353    
354        adDatabase  = null;
355        ocDatabase  = null;
356        environment = null;
357        atDecodeMap = null;
358        aoDecodeMap = null;
359        ocDecodeMap = null;
360        adEncodeMap = null;
361        ocEncodeMap = null;
362        adCounter   = null;
363        ocCounter   = null;
364      }
365    
366    
367    
368      /**
369       * {@inheritDoc}
370       */
371      @Override()
372      public byte[] encodeObjectClasses(Map<ObjectClass,String> objectClasses)
373             throws DirectoryException
374      {
375        ByteArray encodedClasses = ocEncodeMap.get(objectClasses);
376        if (encodedClasses == null)
377        {
378          synchronized (ocEncodeMap)
379          {
380            int setValue = ocCounter.getAndIncrement();
381            byte[] tokenArray = encodeInt(setValue);
382    
383            ArrayList<ASN1Element> elements =
384                 new ArrayList<ASN1Element>(objectClasses.size());
385            for (String ocName : objectClasses.values())
386            {
387              elements.add(new ASN1OctetString(ocName));
388            }
389    
390            byte[] encodedOCs = new ASN1Sequence(elements).encode();
391            store(ocDatabase, tokenArray, encodedOCs);
392    
393            encodedClasses = new ByteArray(tokenArray);
394            ocEncodeMap.put(objectClasses, encodedClasses);
395            ocDecodeMap.put(encodedClasses, objectClasses);
396    
397            return tokenArray;
398          }
399        }
400        else
401        {
402          return encodedClasses.array();
403        }
404      }
405    
406    
407    
408      /**
409       * {@inheritDoc}
410       */
411      @Override()
412      public Map<ObjectClass,String> decodeObjectClasses(
413                                          byte[] encodedObjectClasses)
414             throws DirectoryException
415      {
416        ByteArray byteArray = new ByteArray(encodedObjectClasses);
417        Map<ObjectClass,String> ocMap = ocDecodeMap.get(byteArray);
418        if (ocMap == null)
419        {
420          Message message = ERR_JEB_COMPSCHEMA_UNKNOWN_OC_TOKEN.get(
421                                 bytesToHex(encodedObjectClasses));
422          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
423                                       message);
424        }
425        else
426        {
427          return ocMap;
428        }
429      }
430    
431    
432    
433      /**
434       * {@inheritDoc}
435       */
436      @Override()
437      public byte[] encodeAttribute(Attribute attribute)
438             throws DirectoryException
439      {
440        AttributeType type = attribute.getAttributeType();
441        LinkedHashSet<String> options = attribute.getOptions();
442    
443        ConcurrentHashMap<LinkedHashSet<String>,ByteArray> map =
444             adEncodeMap.get(type);
445        if (map == null)
446        {
447          byte[] tokenArray;
448          synchronized (adEncodeMap)
449          {
450            map = new ConcurrentHashMap<LinkedHashSet<String>,ByteArray>(1);
451    
452            int intValue = adCounter.getAndIncrement();
453            tokenArray = encodeInt(intValue);
454            ByteArray byteArray = new ByteArray(tokenArray);
455            map.put(options,byteArray);
456    
457            ArrayList<ASN1Element> elements =
458                 new ArrayList<ASN1Element>(options.size()+1);
459            elements.add(new ASN1OctetString(attribute.getName()));
460            for (String option : options)
461            {
462              elements.add(new ASN1OctetString(option));
463            }
464            byte[] encodedValue = new ASN1Sequence(elements).encode();
465            store(adDatabase, tokenArray, encodedValue);
466    
467            adEncodeMap.put(type, map);
468            atDecodeMap.put(byteArray, type);
469            aoDecodeMap.put(byteArray, options);
470          }
471    
472          return encodeAttribute(tokenArray, attribute);
473        }
474        else
475        {
476          ByteArray byteArray = map.get(options);
477          if (byteArray == null)
478          {
479            byte[] tokenArray;
480            synchronized (map)
481            {
482              int intValue = adCounter.getAndIncrement();
483              tokenArray = encodeInt(intValue);
484              byteArray = new ByteArray(tokenArray);
485              map.put(options,byteArray);
486    
487              ArrayList<ASN1Element> elements =
488                   new ArrayList<ASN1Element>(options.size()+1);
489              elements.add(new ASN1OctetString(attribute.getName()));
490              for (String option : options)
491              {
492                elements.add(new ASN1OctetString(option));
493              }
494              byte[] encodedValue = new ASN1Sequence(elements).encode();
495              store(adDatabase, tokenArray, encodedValue);
496    
497              atDecodeMap.put(byteArray, type);
498              aoDecodeMap.put(byteArray, options);
499            }
500    
501            return encodeAttribute(tokenArray, attribute);
502          }
503          else
504          {
505            return encodeAttribute(byteArray.array(), attribute);
506          }
507        }
508      }
509    
510    
511    
512      /**
513       * Encodes the information in the provided attribute to a byte
514       * array.
515       *
516       * @param  adArray    The byte array that is a placeholder for the
517       *                    attribute type and set of options.
518       * @param  attribute  The attribute to be encoded.
519       *
520       * @return  An encoded representation of the provided attribute.
521       */
522      private byte[] encodeAttribute(byte[] adArray, Attribute attribute)
523      {
524        LinkedHashSet<AttributeValue> values = attribute.getValues();
525        int totalValuesLength = 0;
526        byte[][] subArrays = new  byte[values.size()*2][];
527        int pos = 0;
528        for (AttributeValue v : values)
529        {
530          byte[] vBytes = v.getValueBytes();
531          byte[] lBytes = ASN1Element.encodeLength(vBytes.length);
532    
533          subArrays[pos++] = lBytes;
534          subArrays[pos++] = vBytes;
535    
536          totalValuesLength += lBytes.length + vBytes.length;
537        }
538    
539        byte[] adArrayLength = ASN1Element.encodeLength(adArray.length);
540        byte[] countBytes = ASN1Element.encodeLength(values.size());
541        int totalLength = adArrayLength.length + adArray.length +
542                          countBytes.length + totalValuesLength;
543        byte[] array = new byte[totalLength];
544    
545        System.arraycopy(adArrayLength, 0, array, 0,
546                         adArrayLength.length);
547        pos = adArrayLength.length;
548        System.arraycopy(adArray, 0, array, pos, adArray.length);
549        pos += adArray.length;
550        System.arraycopy(countBytes, 0, array, pos, countBytes.length);
551        pos += countBytes.length;
552    
553        for (int i=0; i < subArrays.length; i++)
554        {
555          System.arraycopy(subArrays[i], 0, array, pos,
556                           subArrays[i].length);
557          pos += subArrays[i].length;
558        }
559    
560        return array;
561      }
562    
563    
564    
565      /**
566       * {@inheritDoc}
567       */
568      @Override()
569      public Attribute decodeAttribute(byte[] encodedEntry, int startPos,
570                                       int length)
571             throws DirectoryException
572      {
573        // Figure out how many bytes are in the token that is the placeholder for
574        // the attribute description.
575        int pos = startPos;
576        int adArrayLength = encodedEntry[pos] & 0x7F;
577        if (adArrayLength != encodedEntry[pos++])
578        {
579          int numLengthBytes = adArrayLength;
580          adArrayLength = 0;
581          for (int i=0; i < numLengthBytes; i++, pos++)
582          {
583            adArrayLength = (adArrayLength << 8) | (encodedEntry[pos] & 0xFF);
584          }
585        }
586    
587    
588        // Get the attribute description token and make sure it resolves to an
589        // attribute type and option set.
590        ByteArray adArray = new ByteArray(new byte[adArrayLength]);
591        System.arraycopy(encodedEntry, pos, adArray.array(), 0, adArrayLength);
592        pos += adArrayLength;
593        AttributeType attrType = atDecodeMap.get(adArray);
594        LinkedHashSet<String> options = aoDecodeMap.get(adArray);
595        if ((attrType == null) || (options == null))
596        {
597          Message message = ERR_JEB_COMPSCHEMA_UNRECOGNIZED_AD_TOKEN.get(
598                                 bytesToHex(adArray.array()));
599          throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
600                                       message);
601        }
602    
603    
604        // Determine the number of values for the attribute.
605        int numValues = encodedEntry[pos] & 0x7F;
606        if (numValues != encodedEntry[pos++])
607        {
608          int numValuesBytes = numValues;
609          numValues = 0;
610          for (int i=0; i < numValuesBytes; i++, pos++)
611          {
612            numValues = (numValues << 8) | (encodedEntry[pos] & 0xFF);
613          }
614        }
615    
616    
617        // Read the appropriate number of values.
618        LinkedHashSet<AttributeValue> values =
619             new LinkedHashSet<AttributeValue>(numValues);
620        for (int i=0; i < numValues; i++)
621        {
622          int valueLength = encodedEntry[pos] & 0x7F;
623          if (valueLength != encodedEntry[pos++])
624          {
625            int valueLengthBytes = valueLength;
626            valueLength = 0;
627            for (int j=0; j < valueLengthBytes; j++, pos++)
628            {
629              valueLength = (valueLength << 8) | (encodedEntry[pos] & 0xFF);
630            }
631          }
632    
633          byte[] valueBytes = new byte[valueLength];
634          System.arraycopy(encodedEntry, pos, valueBytes, 0, valueLength);
635          pos += valueLength;
636          values.add(new AttributeValue(attrType, new ASN1OctetString(valueBytes)));
637        }
638    
639        return new Attribute(attrType, attrType.getPrimaryName(), options, values);
640      }
641    
642    
643    
644      /**
645       * Stores the provided key-value pair in the specified database container.
646       *
647       * @param  database    The database in which to store the information.
648       * @param  keyBytes    The byte array containing the key to store.
649       * @param  valueBytes  The byte array containing the value to store.
650       *
651       * @throws  DirectoryException  If a problem occurs while attempting to store
652       *                              the data.
653       */
654      private void store(Database database, byte[] keyBytes, byte[] valueBytes)
655              throws DirectoryException
656      {
657        boolean successful = false;
658        DatabaseEntry keyEntry   = new DatabaseEntry(keyBytes);
659        DatabaseEntry valueEntry = new DatabaseEntry(valueBytes);
660    
661        for (int i=0; i < 3; i++)
662        {
663          try
664          {
665            OperationStatus status = database.putNoOverwrite(null, keyEntry,
666                                                             valueEntry);
667            if (status == OperationStatus.SUCCESS)
668            {
669              successful = true;
670              break;
671            }
672            else
673            {
674              Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_STATUS.get(
675                               status.toString());
676              throw new DirectoryException(
677                             DirectoryServer.getServerErrorResultCode(), m);
678            }
679          }
680          catch (DeadlockException de)
681          {
682            continue;
683          }
684          catch (DatabaseException de)
685          {
686            Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_EX.get(de.getMessage());
687            throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
688                                         m, de);
689          }
690        }
691    
692        if (! successful)
693        {
694          Message m = ERR_JEB_COMPSCHEMA_CANNOT_STORE_MULTIPLE_FAILURES.get();
695          throw new DirectoryException(
696                         DirectoryServer.getServerErrorResultCode(), m);
697        }
698      }
699    
700    
701    
702    
703      /**
704       * Encodes the provided int value to a byte array.
705       *
706       * @param  intValue  The int value to be encoded.
707       *
708       * @return  The byte array containing the encoded int value.
709       */
710      private byte[] encodeInt(int intValue)
711      {
712        byte[] array;
713        if (intValue <= 0xFF)
714        {
715          array = new byte[1];
716          array[0] = (byte) (intValue & 0xFF);
717        }
718        else if (intValue <= 0xFFFF)
719        {
720          array = new byte[2];
721          array[0] = (byte) ((intValue >> 8)  & 0xFF);
722          array[1] = (byte) (intValue & 0xFF);
723        }
724        else if (intValue <= 0xFFFFFF)
725        {
726          array = new byte[3];
727          array[0] = (byte) ((intValue >> 16) & 0xFF);
728          array[1] = (byte) ((intValue >> 8)  & 0xFF);
729          array[2] = (byte) (intValue & 0xFF);
730        }
731        else
732        {
733          array = new byte[4];
734          array[0] = (byte) ((intValue >> 24) & 0xFF);
735          array[1] = (byte) ((intValue >> 16) & 0xFF);
736          array[2] = (byte) ((intValue >> 8)  & 0xFF);
737          array[3] = (byte) (intValue & 0xFF);
738        }
739    
740        return array;
741      }
742    
743    
744    
745      /**
746       * Decodes the contents of the provided byte array as an int.
747       *
748       * @param  byteArray  The byte array containing the data to decode.
749       *
750       * @return  The decoded int value.
751       */
752      private int decodeInt(byte[] byteArray)
753      {
754        int intValue = 0;
755    
756        for (byte b : byteArray)
757        {
758          intValue <<= 8;
759          intValue |= (b & 0xFF);
760        }
761    
762        return intValue;
763      }
764    }
765