Package com.sun.messaging.jmq.util

Source Code of com.sun.messaging.jmq.util.PHashMap$ValueCollection

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

/*
* @(#)PHashMap.java  1.19 08/28/07
*/

package com.sun.messaging.jmq.util;

import com.sun.messaging.jmq.resources.*;
import com.sun.messaging.jmq.io.*;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.io.*;

/**
* PHashMap is a persistent HashMap that is backed by a file.
* After instantiated, the PHashMap should be loaded first by calling
* <code>loaded()</code> before any other methods is used.
*/
public class PHashMap extends ConcurrentHashMap {

    private static boolean DEBUG = false;

    public static final int VERSION = 1;

    public static final int DEFAULT_INITIAL_MAP_CAPACITY = 128;

    protected VRFile backingFile;
    protected ConcurrentHashMap recordMap; // maps the key -> VRecord
    protected boolean safe = false;

    // set in VRFileRAF to identify backing file for PHashMap
    protected static long cookie
  = String.valueOf("PHashMap:"+VERSION).hashCode();

    private VRFileWarning warning = null;

    protected boolean loaded = false;

    /**
     * Construct the hash table backed by the specified file.
     * If the file exists, the hash table will be initialized
     * with entries from the file.
     * @param filename  Backing file.
     * @param safe    Indicate whether the underlying file
     *        should be sync'ed as soon as possible.
     * @param reset    If true, all data in the file will be cleared.
     */
    public PHashMap(File filename, boolean safe, boolean reset)
  throws IOException {

  this(filename, VRFileRAF.DEFAULT_INITIAL_FILE_SIZE,
            DEFAULT_INITIAL_MAP_CAPACITY, safe, reset);
    }

    /**
     * Construct the hash table backed by the specified file.
     * If the file exists, the hash table will be initialized
     * with entries from the file.
     * @param filename  Backing file.
     * @param size    Initialize size of backing file.
     * @param safe    Indicate whether the underlying file
     *        should be sync'ed as soon as possible.
     * @param reset    If true, all data in the file will be cleared.
     */
    public PHashMap(File filename, long size, boolean safe, boolean reset)
        throws IOException {

        this(filename, size, DEFAULT_INITIAL_MAP_CAPACITY, safe, reset);
    }

    /**
     * Construct the hash table backed by the specified file.
     * If the file exists, the hash table will be initialized
     * with entries from the file.
     * @param filename  Backing file.
     * @param size    Initialize size of backing file.
     * @param mapCapacity  Initialize map capacity.
     * @param safe    Indicate whether the underlying file
     *        should be sync'ed as soon as possible.
     * @param reset    If true, all data in the file will be cleared.
     */
    public PHashMap(File filename, long size, int mapCapacity,
        boolean safe, boolean reset) throws IOException {

        super(mapCapacity);

  if (DEBUG) {
      System.out.println("PHashMap cookie: " + cookie);
  }

  this.safe = safe;

  initBackingFile(filename, size);
  backingFile.setCookie(cookie);
  backingFile.setSafe(safe);

  if (reset) {
      backingFile.clear(false);
  }

  try {
      backingFile.open();
  } catch (VRFileWarning w) {
      warning = w;
  }
    }

    protected void initBackingFile(File filename, long size) {
  backingFile = new VRFileRAF(filename, size);
    }

    public void load() throws IOException, ClassNotFoundException,
  PHashMapLoadException {

  PHashMapLoadException loadException = null;

  Set entries = backingFile.getRecords();
        int eSize = entries.size();
        int mSize = this.size();
        if (eSize > mSize) {
            recordMap = new ConcurrentHashMap(eSize);
        } else {
            recordMap = new ConcurrentHashMap(mSize);
        }

  Iterator iter = entries.iterator();
  while (iter.hasNext()) {
      VRecordRAF record = (VRecordRAF)iter.next();

      Object key = null;
      Object value = null;
      Throwable kex = null;
      Throwable vex = null;
      Throwable ex = null;
      try {
        byte[] buf = new byte[record.getDataCapacity()];
        record.read(buf);
        ByteArrayInputStream bais = new ByteArrayInputStream(buf);

                // Use our version of ObjectInputStream so we can load old
                // serialized object from an old store, i.e. store migration
                ObjectInputStream ois = new MQObjectInputStream(bais);
        try {
        key = ois.readObject();
        } catch (Throwable e) {
        if (e instanceof ClassNotFoundException) {
      throw (ClassNotFoundException)e;
        } else {
      kex = e;
        }
    }

    try {
        value = ois.readObject();
    } catch (Throwable e) {
        if (e instanceof ClassNotFoundException) {
      throw (ClassNotFoundException)e;
        } else {
      vex = e;
        }
    }
    ois.close();
    bais.close();
      } catch (IOException e) {
    ex = e;
      }

      if (kex != null || vex != null || ex != null) {

    PHashMapLoadException le = new PHashMapLoadException(
                    "Failed to load data in [" + record.toString() + "]");
    le.setKey(key);
    le.setValue(value);
    le.setKeyCause(kex);
    le.setValueCause(vex);
    le.setNextException(loadException);
    le.initCause(ex);
    loadException = le;

    if (key != null && value != null) {
        // we have the key, keep the record
        recordMap.put(key, record);
        super.put(key, value);
    } else {
        // delete bad record
        backingFile.free(record);
    }
      } else {
    // cache info
    recordMap.put(key, record);
    super.put(key, value);
      }
  }

  loaded = true;

  if (loadException != null) {
      throw loadException;
  }
    }

    /**
     * Forces any changes made to the hash map to be written to disk.
     */
    public void force() throws IOException {
  force(null);
    }

    public void force(Object key) throws IOException {
  checkLoaded();

  if (key != null) {
      VRecord record = (VRecord)recordMap.get(key);
      if (record != null) {
    record.force();
      }
  } else {
      // sync the whole file
      backingFile.force();
  }
    }

    public Object put(Object key, Object value) {
        return doPut(key, value, false);
    }

    public Object putIfAbsent(Object key, Object value) {
        return doPut(key, value, true);
    }

    public final Object put(Object key, Object value, boolean putIfAbsent) {
        return doPut(key, value, putIfAbsent);
    }

    /**
     * Maps the specified key to the specified value in this HashMap.
     * The entry will be persisted in the backing file.
     */
    Object doPut(Object key, Object value, boolean putIfAbsent) {
  checkLoaded();

        boolean error = false;
        Object oldValue = null;
  try {
            if (putIfAbsent) {
                oldValue = super.putIfAbsent(key, value);
                if (oldValue != null) {
                    // nothing to do since there was a mapping for key
                    return oldValue;
                }
            } else {
                oldValue = super.put(key, value);
            }

      // serialize the key and value
      ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
      ObjectOutputStream bos = new ObjectOutputStream(baos);

      bos.writeObject(key);
      bos.writeObject(value);
      bos.close();

      byte[] data = baos.toByteArray();

            VRecordRAF record = (VRecordRAF)recordMap.get(key);

            synchronized (backingFile) {
                if (record == null) {
                    record = (VRecordRAF)backingFile.allocate(data.length);
                } else {
                    if (record.getDataCapacity() < data.length) {
                        // need another VRecordRAF
                        backingFile.free(record);
                        record = (VRecordRAF)backingFile.allocate(data.length);
                    } else {
                        record.rewind();
                    }
                }
                record.write(data);

                if (safe) {
                    record.force();
                }
            }

            // update internal records map
            recordMap.put(key, record);
  } catch (IOException e) {
            error = true;
      throw new RuntimeException(e);
  } finally {
            if (error) {
                if (oldValue == null) {
                    super.remove(key);
                } else {
                    super.put(key, oldValue); // put back the old value
                }
            }
        }

        return oldValue;
    }

    public Object remove(Object key) {
  checkLoaded();

        try {
            Object old = super.remove(key);
            removeFromFile(key);
      return old;
  } catch (IOException e) {
      throw new RuntimeException(e);
  }
    }

    public boolean remove(Object key, Object value) {
        throw new UnsupportedOperationException("Operation not supported");
    }

    public Object replace(Object key, Object value) {
        throw new UnsupportedOperationException("Operation not supported");
    }

    public boolean replace(Object key, Object oldValue, Object newValue) {
        throw new UnsupportedOperationException("Operation not supported");
    }

    public void clear() {
  checkLoaded();

  try {
            super.clear();
            recordMap.clear();
            backingFile.clear(false);
  } catch (IOException e) {
      throw new RuntimeException(e);
  }
    }

    public void close() {
  backingFile.close();
    }

    public Set entrySet() {
  checkLoaded();

  Set set = entrySet;
        if (set == null) {
            entrySet = new HashSet(super.entrySet());
            set = entrySet;
        }

  return set;
    }

    public Set keySet() {
  checkLoaded();

  Set set = keySet;
        if (set == null) {
            keySet = new HashSet(super.keySet());
            set = keySet;
        }

  return set;
    }

    public VRFileWarning getWarning() {
  return warning;
    }

    public Collection values() {
  checkLoaded();

  Collection c = values;
        if (c == null) {
            values = new ValueCollection(super.entrySet());
            c = values;
        }
  return c;
    }

    protected final void checkLoaded() {
  if (!loaded) {
      throw new IllegalStateException(
      SharedResources.getResources().getString(
        SharedResources.E_VRFILE_NOT_OPEN));
  }
    }

    final Object putInHashMap(Object key, Object value, boolean putIfAbsent) {
  if (putIfAbsent) {
            return super.putIfAbsent(key, value);
        } else {
            return super.put(key, value);
        }
    }

    // Views: key set, entry set, and value collection

    private Set entrySet = null;
    private Set keySet = null;
    private Collection values = null;

    private void removeFromFile(Object key) throws IOException {
  if (DEBUG) {
      System.out.println("PHashMap.removeFromFile() called for " + key);
  }

  VRecord record = (VRecord)recordMap.remove(key);
  if (record != null) {
            synchronized (backingFile) {
                backingFile.free(record);
                if (safe) {
                    backingFile.force();
                }
            }
  }
    }

    private class HashSet extends AbstractSet {
  Set set = null;
  HashSet(Set set) {
      this.set = set;
  }

  public int size() {
      return set.size();
  }

  public boolean contains(Object o) {
      return set.contains(o);
  }

  public boolean remove(Object o) {
      boolean b = set.remove(o);
      if (b) {
    Object key = null;
    if (o instanceof Map.Entry) {
        key = ((Map.Entry)o).getKey();
    } else {
        key = o;
    }
    try {
        removeFromFile(key);
    } catch (IOException t) {
        throw new RuntimeException(t);
    }
      }
      return b;
  }

  public void clear() {
      PHashMap.this.clear();
  }

  public Iterator iterator() {
      return new HashIterator(set.iterator());
  }
    }

    private class ValueCollection extends AbstractCollection {
  Set entries = null;

  ValueCollection(Set e) {
      this.entries = e;
  }

  public int size() {
      return entries.size();
  }

  public boolean contains(Object o) {
      return containsValue(o);
  }

  public void clear() {
      PHashMap.this.clear();
  }

  public Iterator iterator() {
      return new HashIterator(entries.iterator(), true);
  }
    }

    // Iterator for entry set, key set and values collection
    private class HashIterator implements Iterator {
  boolean values = false;
  Iterator iterator = null;
  Object current = null;

  HashIterator(Iterator itor) {
      this.iterator = itor;
  }

  HashIterator(Iterator itor, boolean values) {
      this.iterator = itor;
      this.values = values;
  }

  public boolean hasNext() {
      return iterator.hasNext();
  }

  public Object next() {
      current = iterator.next();
      if (values) {
    return ((Map.Entry)current).getValue();
      } else {
        return current;
      }
  }

  public void remove() {
      iterator.remove();
      Object key = null;
      if (current instanceof Map.Entry) {
    key = ((Map.Entry)current).getKey();
      } else {
    key = current;
      }
      try {
    removeFromFile(key);
      } catch (IOException t) {
    throw new RuntimeException(t);
      }
  }
    }
}
TOP

Related Classes of com.sun.messaging.jmq.util.PHashMap$ValueCollection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.