1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.io.InputStreamFile |
4 | |
5 | Copyright 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | 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 | |
21 | package org.apache.derby.impl.io; |
22 | |
23 | import org.apache.derby.io.StorageFactory; |
24 | import org.apache.derby.io.StorageFile; |
25 | import org.apache.derby.io.StorageRandomAccessFile; |
26 | |
27 | import org.apache.derby.iapi.services.sanity.SanityManager; |
28 | |
29 | import java.io.File; |
30 | import java.io.InputStream; |
31 | import java.io.OutputStream; |
32 | import java.io.IOException; |
33 | import java.io.FileNotFoundException; |
34 | import java.net.MalformedURLException; |
35 | import java.net.URL; |
36 | |
37 | /** |
38 | * This class provides the base for read-only stream implementations of the StorageFile interface. It is used with the |
39 | * classpath, jar, http, and https subsubprotocols |
40 | */ |
41 | abstract class InputStreamFile implements StorageFile |
42 | { |
43 | |
44 | final String path; |
45 | final int nameStart; // getName() = path.substring( nameStart) |
46 | final BaseStorageFactory storageFactory; |
47 | |
48 | InputStreamFile( BaseStorageFactory storageFactory, String path) |
49 | { |
50 | this.storageFactory = storageFactory; |
51 | if( path == null || path.length() == 0) |
52 | { |
53 | this.path = storageFactory.dataDirectory; |
54 | nameStart = -1; |
55 | } |
56 | else |
57 | { |
58 | StringBuffer sb = new StringBuffer( storageFactory.separatedDataDirectory); |
59 | if( File.separatorChar != '/') |
60 | sb.append( path.replace( File.separatorChar, '/')); |
61 | else |
62 | sb.append( path); |
63 | this.path = sb.toString(); |
64 | nameStart = this.path.lastIndexOf( '/') + 1; |
65 | } |
66 | } |
67 | |
68 | InputStreamFile( BaseStorageFactory storageFactory, String parent, String name) |
69 | { |
70 | this.storageFactory = storageFactory; |
71 | StringBuffer sb = new StringBuffer( storageFactory.separatedDataDirectory); |
72 | if( File.separatorChar != '/') |
73 | { |
74 | sb.append( parent.replace( File.separatorChar, '/')); |
75 | sb.append( '/'); |
76 | sb.append( name.replace( File.separatorChar, '/')); |
77 | } |
78 | else |
79 | { |
80 | sb.append( parent); |
81 | sb.append( '/'); |
82 | sb.append( name); |
83 | } |
84 | path = sb.toString(); |
85 | nameStart = this.path.lastIndexOf( '/') + 1; |
86 | } |
87 | |
88 | InputStreamFile( InputStreamFile dir, String name) |
89 | { |
90 | this.storageFactory = dir.storageFactory; |
91 | StringBuffer sb = new StringBuffer( dir.path); |
92 | sb.append( '/'); |
93 | if( File.separatorChar != '/') |
94 | sb.append( name.replace( File.separatorChar, '/')); |
95 | else |
96 | sb.append( name); |
97 | path = sb.toString(); |
98 | nameStart = this.path.lastIndexOf( '/') + 1; |
99 | } |
100 | |
101 | InputStreamFile( BaseStorageFactory storageFactory, String child, int pathLen) |
102 | { |
103 | this.storageFactory = storageFactory; |
104 | path = child.substring( 0, pathLen); |
105 | nameStart = this.path.lastIndexOf( '/') + 1; |
106 | } |
107 | |
108 | public boolean equals( Object other) |
109 | { |
110 | if( other == null || ! getClass().equals( other.getClass())) |
111 | return false; |
112 | InputStreamFile otherFile = (InputStreamFile) other; |
113 | return path.equals( otherFile.path); |
114 | } |
115 | |
116 | public int hashCode() |
117 | { |
118 | return path.hashCode(); |
119 | } |
120 | |
121 | /** |
122 | * Get the names of all files and sub-directories in the directory named by this path name. |
123 | * |
124 | * @return An array of the names of the files and directories in this |
125 | * directory denoted by this abstract pathname. The returned array will have length 0 |
126 | * if this directory is empty. Returns null if this StorageFile is not a directory, or |
127 | * if an I/O error occurs. |
128 | */ |
129 | public String[] list() |
130 | { |
131 | return null; |
132 | } |
133 | |
134 | /** |
135 | * Determine whether the named file is writable. |
136 | * |
137 | * @return <b>true</b> if the file exists and is writable, <b>false</b> if not. |
138 | */ |
139 | public boolean canWrite() |
140 | { |
141 | return false; |
142 | } |
143 | |
144 | /** |
145 | * Tests whether the named file exists. |
146 | * |
147 | * @return <b>true</b> if the named file exists, <b>false</b> if not. |
148 | */ |
149 | public abstract boolean exists(); |
150 | |
151 | /** |
152 | * Tests whether the named file is a directory, or not. This is only called in writable storage factories. |
153 | * |
154 | * @return <b>true</b> if named file exists and is a directory, <b>false</b> if not. |
155 | * The return value is undefined if the storage is read-only. |
156 | */ |
157 | public boolean isDirectory() |
158 | { |
159 | return false; |
160 | } |
161 | |
162 | /** |
163 | * Deletes the named file or empty directory. This method does not delete non-empty directories. |
164 | * |
165 | * @return <b>true</b> if the named file or directory is successfully deleted, <b>false</b> if not |
166 | */ |
167 | public boolean delete() |
168 | { |
169 | return false; |
170 | } |
171 | |
172 | /** |
173 | * Deletes the named file and, if it is a directory, all the files and directories it contains. |
174 | * |
175 | * @return <b>true</b> if the named file or directory is successfully deleted, <b>false</b> if not |
176 | */ |
177 | public boolean deleteAll() |
178 | { |
179 | return false; |
180 | } |
181 | |
182 | /** |
183 | * Converts this StorageFile into a pathname string. The character returned by StorageFactory.getSeparator() |
184 | * is used to separate the directory and file names in the sequence. |
185 | * |
186 | *<p> |
187 | *<b>The returned path may include the database directory. Therefore it cannot be directly used to make an StorageFile |
188 | * equivalent to this one.</b> |
189 | * |
190 | * @return The pathname as a string. |
191 | * |
192 | * @see StorageFactory#getSeparator |
193 | */ |
194 | public String getPath() |
195 | { |
196 | if( File.separatorChar != '/') |
197 | return path.replace( '/', File.separatorChar); |
198 | return path; |
199 | } // end of getPath |
200 | |
201 | public String getCanonicalPath() throws IOException |
202 | { |
203 | return storageFactory.getCanonicalName() + "/" + path; |
204 | } |
205 | |
206 | /** |
207 | * @return The last segment in the path name, "" if the path name sequence is empty. |
208 | */ |
209 | public String getName() |
210 | { |
211 | return (nameStart < 0) ? "" : path.substring( nameStart); |
212 | } |
213 | |
214 | /** |
215 | * If the named file does not already exist then create it as an empty normal file. |
216 | * |
217 | * The implementation |
218 | * must synchronize with other threads accessing the same file (in the same or a different process). |
219 | * If two threads both attempt to create a file with the same name |
220 | * at the same time then at most one should succeed. |
221 | * |
222 | * @return <b>true</b> if this thread's invocation of createNewFile successfully created the named file; |
223 | * <b>false</b> if not, i.e. <b>false</b> if the named file already exists or if another concurrent thread created it. |
224 | * |
225 | * @exception IOException - If the directory does not exist or some other I/O error occurred |
226 | */ |
227 | public boolean createNewFile() throws IOException |
228 | { |
229 | throw new IOException( "createNewFile called in a read-only file system."); |
230 | } |
231 | |
232 | /** |
233 | * Rename the file denoted by this name. Note that StorageFile objects are immutable. This method |
234 | * renames the underlying file, it does not change this StorageFile object. The StorageFile object denotes the |
235 | * same name as before, however the exists() method will return false after the renameTo method |
236 | * executes successfully. |
237 | * |
238 | *<p>It is not specified whether this method will succeed if a file already exists under the new name. |
239 | * |
240 | * @param newName the new name. |
241 | * |
242 | * @return <b>true</b> if the rename succeeded, <b>false</b> if not. |
243 | */ |
244 | public boolean renameTo( StorageFile newName) |
245 | { |
246 | return false; |
247 | } |
248 | |
249 | /** |
250 | * Creates the named directory. |
251 | * |
252 | * @return <b>true</b> if the directory was created; <b>false</b> if not. |
253 | */ |
254 | public boolean mkdir() |
255 | { |
256 | return false; |
257 | } |
258 | |
259 | /** |
260 | * Creates the named directory, and all nonexistent parent directories. |
261 | * |
262 | * @return <b>true</b> if the directory was created, <b>false</b> if not |
263 | */ |
264 | public boolean mkdirs() |
265 | { |
266 | return false; |
267 | } |
268 | |
269 | /** |
270 | * Returns the length of the named file if it is not a directory. The return value is not specified |
271 | * if the file is a directory. |
272 | * |
273 | * @return The length, in bytes, of the named file if it exists and is not a directory, |
274 | * 0 if the file does not exist, or any value if the named file is a directory. |
275 | */ |
276 | public long length() |
277 | { |
278 | try |
279 | { |
280 | InputStream is = getInputStream(); |
281 | if( is == null) |
282 | return 0; |
283 | long len = is.available(); |
284 | is.close(); |
285 | return len; |
286 | } |
287 | catch( IOException e){ return 0;} |
288 | } // end of length |
289 | |
290 | /** |
291 | * Get the name of the parent directory if this name includes a parent. |
292 | * |
293 | * @return An StorageFile denoting the parent directory of this StorageFile, if it has a parent, null if |
294 | * it does not have a parent. |
295 | */ |
296 | public StorageFile getParentDir() |
297 | { |
298 | if( path.length() <= storageFactory.separatedDataDirectory.length()) |
299 | return null; |
300 | return getParentDir( path.lastIndexOf( '/')); |
301 | } |
302 | |
303 | /** |
304 | * Get the parent of this file. |
305 | * |
306 | * @param pathLen the length of the parent's path name. |
307 | */ |
308 | abstract StorageFile getParentDir( int pathLen); |
309 | |
310 | /** |
311 | * Make the named file or directory read-only. This interface does not specify whether this |
312 | * also makes the file undeletable. |
313 | * |
314 | * @return <b>true</b> if the named file or directory was made read-only, or it already was read-only; |
315 | * <b>false</b> if not. |
316 | */ |
317 | public boolean setReadOnly() |
318 | { |
319 | return true; |
320 | } |
321 | |
322 | /** |
323 | * Creates an output stream from a file name. If a normal file already exists with this name it |
324 | * will first be truncated to zero length. |
325 | * |
326 | * @return an output stream suitable for writing to the file. |
327 | * |
328 | * @exception FileNotFoundException if the file exists but is a directory |
329 | * rather than a regular file, does not exist but cannot be created, or |
330 | * cannot be opened for any other reason. |
331 | */ |
332 | public OutputStream getOutputStream( ) throws FileNotFoundException |
333 | { |
334 | throw new FileNotFoundException( "Attempt to write into a read only file system."); |
335 | } |
336 | |
337 | |
338 | /** |
339 | * Creates an output stream from a file name. If a normal file already exists with this name it |
340 | * will first be truncated to zero length. |
341 | * |
342 | * @return an output stream suitable for writing to the file. |
343 | * |
344 | * @exception FileNotFoundException if the file exists but is a directory |
345 | * rather than a regular file, does not exist but cannot be created, or |
346 | * cannot be opened for any other reason. |
347 | */ |
348 | public OutputStream getOutputStream( boolean append) throws FileNotFoundException |
349 | { |
350 | throw new FileNotFoundException( "Attempt to write into a read only file system."); |
351 | } |
352 | |
353 | |
354 | /** |
355 | * Creates an input stream from a file name. |
356 | * |
357 | * @return an input stream suitable for reading from the file. |
358 | * |
359 | * @exception FileNotFoundException if the file is not found. |
360 | */ |
361 | abstract public InputStream getInputStream( ) throws FileNotFoundException; |
362 | |
363 | /** |
364 | * Get an exclusive lock with this name. This is used to ensure that two or more JVMs do not open the same database |
365 | * at the same time. |
366 | * |
367 | * @return EXCLUSIVE_FILE_LOCK_NOT_AVAILABLE if the lock cannot be acquired because it is already held.<br> |
368 | * EXCLUSIVE_FILE_LOCK if the lock was successfully acquired.<br> |
369 | * NO_FILE_LOCK_SUPPORT if the system does not support exclusive locks.<br> |
370 | */ |
371 | public int getExclusiveFileLock() |
372 | { |
373 | return NO_FILE_LOCK_SUPPORT; |
374 | } |
375 | |
376 | /** |
377 | * Release the resource associated with an earlier acquired exclusive lock |
378 | * |
379 | * @see #getExclusiveFileLock |
380 | */ |
381 | public void releaseExclusiveFileLock() |
382 | {} |
383 | |
384 | /** |
385 | * Get a random access file. |
386 | * |
387 | * @param mode "r", "rw", "rws", or "rwd". The "rws" and "rwd" modes specify |
388 | * that the data is to be written to persistent store, consistent with the |
389 | * java.io.RandomAccessFile class ("synchronized" with the persistent |
390 | * storage, in the file system meaning of the word "synchronized"). However |
391 | * the implementation is not required to implement the "rws" or "rwd" |
392 | * modes. The implementation may treat "rws" and "rwd" as "rw". It is up to |
393 | * the user of this interface to call the StorageRandomAccessFile.sync |
394 | * method. However, if the "rws" or "rwd" modes are supported and the |
395 | * RandomAccessFile was opened in "rws" or "rwd" mode then the |
396 | * implementation of StorageRandomAccessFile.sync need not do anything. |
397 | * |
398 | * @return an object that can be used for random access to the file. |
399 | * |
400 | * @exception IllegalArgumentException if the mode argument is not equal to one of "r", "rw", "rws", or "rwd". |
401 | * @exception FileNotFoundException if the file exists but is a directory rather than a regular |
402 | * file, or cannot be opened or created for any other reason . |
403 | * |
404 | * @see <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/io/RandomAccessFile.html">java.io.RandomAccessFile</a> |
405 | */ |
406 | public StorageRandomAccessFile getRandomAccessFile( String mode) throws FileNotFoundException |
407 | { |
408 | if( SanityManager.DEBUG) |
409 | SanityManager.NOTREACHED(); |
410 | return null; |
411 | } |
412 | |
413 | /** |
414 | * Get the file name for diagnostic purposes. Usually the same as getPath(). |
415 | * |
416 | * @return the file name |
417 | */ |
418 | public String toString() |
419 | { |
420 | return path; |
421 | } |
422 | |
423 | /** |
424 | * @see org.apache.derby.io.StorageFile#getURL() |
425 | */ |
426 | public URL getURL() throws MalformedURLException { |
427 | throw new MalformedURLException(toString()); |
428 | } |
429 | } |