001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.fusesource.hawtdb.api;
018    
019    import org.fusesource.hawtbuf.codec.Codec;
020    import org.fusesource.hawtbuf.codec.ObjectCodec;
021    import org.fusesource.hawtdb.internal.index.HashIndex;
022    
023    /**
024     * <p>
025     * Uses to create Hash based storage of key/values.  The hash index
026     * consists of contiguous array of pages allocated on the page file.
027     * Each page is considered a bucket.  keys get hashed to a bucket 
028     * indexes and the key and value are stored in the bucket.  Each 
029     * bucket is actually a BTree root and can therefore store multiple 
030     * keys and values and overflow to additional pages if needed.    
031     * </p>
032     * <p>
033     * Once the percentage of hash buckets used passes the configured
034     * load factor, the hash index will "resize" to use a larger number
035     * of buckets to keep the number items per bucket low which increases
036     * access time of the items as there are fewer page access required.
037     * </p>
038     * <p>
039     * Unlike BTree indexes, Hash indexes are not kept in key sorted order.
040     * </p>
041     * 
042     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
043     */
044    public class HashIndexFactory<Key, Value> implements IndexFactory<Key, Value> {
045        
046        public static final String PROPERTY_PREFIX = HashIndex.class.getName()+".";
047        public static final int DEFAULT_BUCKET_CAPACITY = Integer.parseInt(System.getProperty(PROPERTY_PREFIX+"DEFAULT_BUCKET_CAPACITY", "1024"));
048        public static final int DEFAULT_MAXIMUM_BUCKET_CAPACITY = Integer.parseInt(System.getProperty(PROPERTY_PREFIX+"DEFAULT_MAXIMUM_BUCKET_CAPACITY", "16384"));
049        public static final int DEFAULT_MINIMUM_BUCKET_CAPACITY = Integer.parseInt(System.getProperty(PROPERTY_PREFIX+"DEFAULT_MINIMUM_BUCKET_CAPACITY", "16"));
050        public static final int DEFAULT_LOAD_FACTOR = Integer.parseInt(System.getProperty(PROPERTY_PREFIX+"DEFAULT_LOAD_FACTOR", "75"));
051        
052        private Codec<Key> keyCodec = new ObjectCodec<Key>();
053        private Codec<Value> valueCodec = new ObjectCodec<Value>();
054        private int initialBucketCapacity = DEFAULT_BUCKET_CAPACITY;
055        private int maximumBucketCapacity = DEFAULT_MAXIMUM_BUCKET_CAPACITY;
056        private int minimumBucketCapacity = DEFAULT_MINIMUM_BUCKET_CAPACITY;
057        private int loadFactor = DEFAULT_LOAD_FACTOR;
058        private boolean deferredEncoding=true;
059    
060        /**
061         * Loads an existing hash index from the paged object.
062         */
063        public Index<Key, Value> open(Paged paged, int indexNumber) {
064            return createInstance(paged, indexNumber).open();
065        }
066    
067        /**
068         * Loads an existing hash index from the paged object.
069         */
070        public Index<Key, Value> open(Paged paged) {
071            return createInstance(paged, 0).open();
072        }
073    
074        /**
075         * Creates a new hash index on the Paged object.
076         */
077        public Index<Key, Value> create(Paged paged) {
078            return createInstance(paged, paged.alloc()).create();
079        }
080    
081        private HashIndex<Key, Value> createInstance(Paged paged, int page) {
082            return new HashIndex<Key, Value>(paged, page, this);
083        }
084    
085        /**
086         * Defaults to an {@link org.fusesource.hawtbuf.codec.ObjectCodec} if not explicitly set.
087         * 
088         * @return the marshaller used for keys.
089         */
090        public Codec<Key> getKeyCodec() {
091            return keyCodec;
092        }
093    
094        /**
095         * Allows you to configure custom marshalling logic to encode the index keys.
096         * 
097         * @param codec the marshaller used for keys.
098         */
099        public void setKeyCodec(Codec<Key> codec) {
100            this.keyCodec = codec;
101        }
102    
103        /**
104         * Defaults to an {@link org.fusesource.hawtbuf.codec.ObjectCodec} if not explicitly set.
105         *  
106         * @return the marshaller used for values.
107         */
108        public Codec<Value> getValueCodec() {
109            return valueCodec;
110        }
111    
112        /**
113         * Allows you to configure custom marshalling logic to encode the index values.
114         * 
115         * @param codec the marshaller used for values
116         */
117        public void setValueCodec(Codec<Value> codec) {
118            this.valueCodec = codec;
119        }
120    
121        /**
122         * @return the maximum bucket capacity
123         */
124        public int getMaximumBucketCapacity() {
125            return maximumBucketCapacity;
126        }
127    
128        /**
129         * Sets the maximum bucket capacity.
130         * 
131         * @param value the new capacity
132         */
133        public void setMaximumBucketCapacity(int value) {
134            this.maximumBucketCapacity = value;
135        }
136    
137        /**
138         * 
139         * @return the minimum bucket capacity
140         */
141        public int getMinimumBucketCapacity() {
142            return minimumBucketCapacity;
143        }
144    
145        /**
146         * Sets the minimum bucket capacity.
147         * 
148         * @param value the new capacity
149         */
150        public void setMinimumBucketCapacity(int value) {
151            this.minimumBucketCapacity = value;
152        }
153    
154        /**
155         * @return the index load factor
156         */
157        public int getLoadFactor() {
158            return loadFactor;
159        }
160    
161        /**
162         * Sets the index load factor.  When this load factor percentage
163         * of used buckets is execeeded, the index will resize to increase the bucket capacity. 
164         * @param loadFactor
165         */
166        public void setLoadFactor(int loadFactor) {
167            this.loadFactor = loadFactor;
168        }
169    
170        /**
171         * @return the initial bucket capacity
172         */
173        public int getBucketCapacity() {
174            return initialBucketCapacity;
175        }
176    
177        /**
178         * sets the initial bucket capacity. 
179         * @param binCapacity
180         */
181        public void setBucketCapacity(int binCapacity) {
182            this.initialBucketCapacity = binCapacity;
183        }
184    
185        /**
186         * Convenience method which sets the maximum, minimum and initial bucket capacity to be the specified value.
187         * @param value
188         */
189        public void setFixedCapacity(int value) {
190            this.minimumBucketCapacity = this.maximumBucketCapacity = this.initialBucketCapacity = value;
191        }
192    
193        /**
194         * 
195         * @return true if deferred encoding enabled
196         */
197        public boolean isDeferredEncoding() {
198            return deferredEncoding;
199        }
200    
201        /**
202         * <p>
203         * When deferred encoding is enabled, the index avoids encoding keys and values
204         * for as long as possible so take advantage of collapsing multiple updates of the 
205         * same key/value into a single update operation and single encoding operation.
206         * </p><p>
207         * Using this feature requires the keys and values to be immutable objects since 
208         * unexpected errors would occur if they are changed after they have been handed
209         * to to the index for storage. 
210         * </p>
211         * @param enable should deferred encoding be enabled.
212         */
213        public void setDeferredEncoding(boolean enable) {
214            this.deferredEncoding = enable;
215        }
216        
217    }