1   package org.apache.lucene.store;
2   
3   /**
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  import java.io.IOException;
21  import java.io.FileNotFoundException;
22  import java.io.File;
23  import java.io.Serializable;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.Set;
27  
28  /**
29   * A memory-resident {@link Directory} implementation.  Locking
30   * implementation is by default the {@link SingleInstanceLockFactory}
31   * but can be changed with {@link #setLockFactory}.
32   *
33   * @version $Id: RAMDirectory.java 675485 2008-07-10 09:27:44Z mikemccand $
34   */
35  public class RAMDirectory extends Directory implements Serializable {
36  
37    private static final long serialVersionUID = 1l;
38  
39    HashMap fileMap = new HashMap();
40    long sizeInBytes = 0;
41    
42    // *****
43    // Lock acquisition sequence:  RAMDirectory, then RAMFile
44    // *****
45  
46    /** Constructs an empty {@link Directory}. */
47    public RAMDirectory() {
48      setLockFactory(new SingleInstanceLockFactory());
49    }
50  
51    /**
52     * Creates a new <code>RAMDirectory</code> instance from a different
53     * <code>Directory</code> implementation.  This can be used to load
54     * a disk-based index into memory.
55     * <P>
56     * This should be used only with indices that can fit into memory.
57     * <P>
58     * Note that the resulting <code>RAMDirectory</code> instance is fully
59     * independent from the original <code>Directory</code> (it is a
60     * complete copy).  Any subsequent changes to the
61     * original <code>Directory</code> will not be visible in the
62     * <code>RAMDirectory</code> instance.
63     *
64     * @param dir a <code>Directory</code> value
65     * @exception IOException if an error occurs
66     */
67    public RAMDirectory(Directory dir) throws IOException {
68      this(dir, false);
69    }
70    
71    private RAMDirectory(Directory dir, boolean closeDir) throws IOException {
72      this();
73      Directory.copy(dir, this, closeDir);
74    }
75  
76    /**
77     * Creates a new <code>RAMDirectory</code> instance from the {@link FSDirectory}.
78     *
79     * @param dir a <code>File</code> specifying the index directory
80     *
81     * @see #RAMDirectory(Directory)
82     */
83    public RAMDirectory(File dir) throws IOException {
84      this(FSDirectory.getDirectory(dir), true);
85    }
86  
87    /**
88     * Creates a new <code>RAMDirectory</code> instance from the {@link FSDirectory}.
89     *
90     * @param dir a <code>String</code> specifying the full index directory path
91     *
92     * @see #RAMDirectory(Directory)
93     */
94    public RAMDirectory(String dir) throws IOException {
95      this(FSDirectory.getDirectory(dir), true);
96    }
97  
98    /** Returns an array of strings, one for each file in the directory. */
99    public synchronized final String[] list() {
100     ensureOpen();
101     Set fileNames = fileMap.keySet();
102     String[] result = new String[fileNames.size()];
103     int i = 0;
104     Iterator it = fileNames.iterator();
105     while (it.hasNext())
106       result[i++] = (String)it.next();
107     return result;
108   }
109 
110   /** Returns true iff the named file exists in this directory. */
111   public final boolean fileExists(String name) {
112     ensureOpen();
113     RAMFile file;
114     synchronized (this) {
115       file = (RAMFile)fileMap.get(name);
116     }
117     return file != null;
118   }
119 
120   /** Returns the time the named file was last modified.
121    * @throws IOException if the file does not exist
122    */
123   public final long fileModified(String name) throws IOException {
124     ensureOpen();
125     RAMFile file;
126     synchronized (this) {
127       file = (RAMFile)fileMap.get(name);
128     }
129     if (file==null)
130       throw new FileNotFoundException(name);
131     return file.getLastModified();
132   }
133 
134   /** Set the modified time of an existing file to now.
135    * @throws IOException if the file does not exist
136    */
137   public void touchFile(String name) throws IOException {
138     ensureOpen();
139     RAMFile file;
140     synchronized (this) {
141       file = (RAMFile)fileMap.get(name);
142     }
143     if (file==null)
144       throw new FileNotFoundException(name);
145     
146     long ts2, ts1 = System.currentTimeMillis();
147     do {
148       try {
149         Thread.sleep(0, 1);
150       } catch (InterruptedException e) {}
151       ts2 = System.currentTimeMillis();
152     } while(ts1 == ts2);
153     
154     file.setLastModified(ts2);
155   }
156 
157   /** Returns the length in bytes of a file in the directory.
158    * @throws IOException if the file does not exist
159    */
160   public final long fileLength(String name) throws IOException {
161     ensureOpen();
162     RAMFile file;
163     synchronized (this) {
164       file = (RAMFile)fileMap.get(name);
165     }
166     if (file==null)
167       throw new FileNotFoundException(name);
168     return file.getLength();
169   }
170   
171   /** Return total size in bytes of all files in this
172    * directory.  This is currently quantized to
173    * RAMOutputStream.BUFFER_SIZE. */
174   public synchronized final long sizeInBytes() {
175     ensureOpen();
176     return sizeInBytes;
177   }
178   
179   /** Removes an existing file in the directory.
180    * @throws IOException if the file does not exist
181    */
182   public synchronized void deleteFile(String name) throws IOException {
183     ensureOpen();
184     RAMFile file = (RAMFile)fileMap.get(name);
185     if (file!=null) {
186         fileMap.remove(name);
187         file.directory = null;
188         sizeInBytes -= file.sizeInBytes;       // updates to RAMFile.sizeInBytes synchronized on directory
189     } else
190       throw new FileNotFoundException(name);
191   }
192 
193   /** Renames an existing file in the directory.
194    * @throws FileNotFoundException if from does not exist
195    * @deprecated
196    */
197   public synchronized final void renameFile(String from, String to) throws IOException {
198     ensureOpen();
199     RAMFile fromFile = (RAMFile)fileMap.get(from);
200     if (fromFile==null)
201       throw new FileNotFoundException(from);
202     RAMFile toFile = (RAMFile)fileMap.get(to);
203     if (toFile!=null) {
204       sizeInBytes -= toFile.sizeInBytes;       // updates to RAMFile.sizeInBytes synchronized on directory
205       toFile.directory = null;
206     }
207     fileMap.remove(from);
208     fileMap.put(to, fromFile);
209   }
210 
211   /** Creates a new, empty file in the directory with the given name. Returns a stream writing this file. */
212   public IndexOutput createOutput(String name) throws IOException {
213     ensureOpen();
214     RAMFile file = new RAMFile(this);
215     synchronized (this) {
216       RAMFile existing = (RAMFile)fileMap.get(name);
217       if (existing!=null) {
218         sizeInBytes -= existing.sizeInBytes;
219         existing.directory = null;
220       }
221       fileMap.put(name, file);
222     }
223     return new RAMOutputStream(file);
224   }
225 
226   /** Returns a stream reading an existing file. */
227   public IndexInput openInput(String name) throws IOException {
228     ensureOpen();
229     RAMFile file;
230     synchronized (this) {
231       file = (RAMFile)fileMap.get(name);
232     }
233     if (file == null)
234       throw new FileNotFoundException(name);
235     return new RAMInputStream(file);
236   }
237 
238   /** Closes the store to future operations, releasing associated memory. */
239   public void close() {
240     isOpen = false;
241     fileMap = null;
242   }
243 }
244