1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.james.mailrepository.filepair;
23
24 import org.apache.avalon.cornerstone.services.store.Repository;
25 import org.apache.avalon.framework.activity.Initializable;
26 import org.apache.avalon.framework.configuration.Configurable;
27 import org.apache.avalon.framework.configuration.Configuration;
28 import org.apache.avalon.framework.configuration.ConfigurationException;
29 import org.apache.avalon.framework.logger.AbstractLogEnabled;
30 import org.apache.avalon.framework.service.ServiceException;
31 import org.apache.avalon.framework.service.ServiceManager;
32 import org.apache.avalon.framework.service.Serviceable;
33 import org.apache.james.services.FileSystem;
34 import org.apache.james.util.io.ExtensionFileFilter;
35
36 import java.io.File;
37 import java.io.FileInputStream;
38 import java.io.FileNotFoundException;
39 import java.io.FileOutputStream;
40 import java.io.FilenameFilter;
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.io.OutputStream;
44 import java.util.ArrayList;
45 import java.util.Iterator;
46
47
48
49
50
51 public abstract class AbstractFileRepository
52 extends AbstractLogEnabled
53 implements Repository, Serviceable, Configurable, Initializable
54 {
55 protected static final boolean DEBUG = false;
56
57 protected static final String HANDLED_URL = "file://";
58 protected static final int BYTE_MASK = 0x0f;
59 protected static final char[] HEX_DIGITS = new char[]
60 {
61 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
62 };
63
64 protected String m_path;
65 protected String m_destination;
66 protected String m_extension;
67 protected String m_name;
68 protected FilenameFilter m_filter;
69 protected File m_baseDirectory;
70
71 protected ServiceManager m_serviceManager;
72
73 protected abstract String getExtensionDecorator();
74
75
76
77
78 public void service( final ServiceManager serviceManager )
79 throws ServiceException
80 {
81 m_serviceManager = serviceManager;
82 try {
83 m_baseDirectory = ((FileSystem) serviceManager.lookup(FileSystem.ROLE)).getBasedir();
84 } catch (FileNotFoundException e) {
85 throw new ServiceException(FileSystem.ROLE, "Cannot find the base directory of the application", e);
86 }
87 }
88
89
90
91
92 public void configure( final Configuration configuration )
93 throws ConfigurationException
94 {
95 if( null == m_destination )
96 {
97 final String destination = configuration.getAttribute( "destinationURL" );
98 setDestination( destination );
99 }
100 }
101
102
103
104
105 public void initialize()
106 throws Exception
107 {
108 getLogger().info( "Init " + getClass().getName() + " Store" );
109
110 m_name = "Repository";
111 String m_postfix = getExtensionDecorator();
112 m_extension = "." + m_name + m_postfix;
113 m_filter = new ExtensionFileFilter(m_extension);
114
115
116 final File directory = new File( m_path );
117 directory.mkdirs();
118
119 getLogger().info( getClass().getName() + " opened in " + m_path );
120
121
122
123
124
125 FilenameFilter num_filter = new NumberedRepositoryFileFilter(getExtensionDecorator());
126 final String[] names = directory.list( num_filter );
127
128 try {
129 for( int i = 0; i < names.length; i++ ) {
130 String origFilename = names[i];
131
132
133 int pos = origFilename.length() - m_postfix.length();
134 while (pos >= 1 && Character.isDigit(origFilename.charAt(pos - 1))) {
135 pos--;
136 }
137 pos -= ".".length() + m_name.length();
138 String newFilename = origFilename.substring(0, pos) + m_extension;
139
140 File origFile = new File(directory, origFilename);
141 File newFile = new File(directory, newFilename);
142
143 if (origFile.renameTo(newFile)) {
144 getLogger().info("Renamed " + origFile + " to " + newFile);
145 } else {
146 getLogger().info("Unable to rename " + origFile + " to " + newFile);
147 }
148 }
149 } catch (Exception e) {
150 e.printStackTrace();
151 throw e;
152 }
153
154 }
155
156
157
158
159
160
161
162 protected void setDestination( final String destination )
163 throws ConfigurationException
164 {
165 if( !destination.startsWith( HANDLED_URL ) )
166 {
167 throw new ConfigurationException( "cannot handle destination " + destination );
168 }
169
170 m_path = destination.substring( HANDLED_URL.length() );
171
172 File directory;
173
174
175 if( m_path.startsWith( "/" ) )
176 {
177 directory = new File( m_path );
178 }
179 else
180 {
181 directory = new File( m_baseDirectory, m_path );
182 }
183
184 try
185 {
186 directory = directory.getCanonicalFile();
187 }
188 catch( final IOException ioe )
189 {
190 throw new ConfigurationException( "Unable to form canonical representation of " +
191 directory );
192 }
193
194 m_path = directory.toString();
195
196 m_destination = destination;
197 }
198
199
200
201
202
203
204
205 protected AbstractFileRepository createChildRepository()
206 throws Exception
207 {
208 return (AbstractFileRepository) getClass().newInstance();
209 }
210
211
212
213
214 public Repository getChildRepository( final String childName )
215 {
216 AbstractFileRepository child = null;
217
218 try
219 {
220 child = createChildRepository();
221 }
222 catch( final Exception e )
223 {
224 throw new RuntimeException( "Cannot create child repository " +
225 childName + " : " + e );
226 }
227
228 try
229 {
230 child.service( m_serviceManager );
231 }
232 catch( final ServiceException cme )
233 {
234 throw new RuntimeException( "Cannot service child " +
235 "repository " + childName +
236 " : " + cme );
237 }
238
239 try
240 {
241 child.setDestination( m_destination + File.pathSeparatorChar +
242 childName + File.pathSeparator );
243 }
244 catch( final ConfigurationException ce )
245 {
246 throw new RuntimeException( "Cannot set destination for child child " +
247 "repository " + childName +
248 " : " + ce );
249 }
250
251 try
252 {
253 child.initialize();
254 }
255 catch( final Exception e )
256 {
257 throw new RuntimeException( "Cannot initialize child " +
258 "repository " + childName +
259 " : " + e );
260 }
261
262 if( DEBUG )
263 {
264 getLogger().debug( "Child repository of " + m_name + " created in " +
265 m_destination + File.pathSeparatorChar +
266 childName + File.pathSeparator );
267 }
268
269 return child;
270 }
271
272
273
274
275
276
277
278
279 protected File getFile( final String key )
280 throws IOException
281 {
282 return new File( encode( key ) );
283 }
284
285
286
287
288
289
290
291
292 protected InputStream getInputStream( final String key )
293 throws IOException
294 {
295
296
297
298
299
300 return new FileInputStream( encode( key ) );
301 }
302
303
304
305
306
307
308
309
310 protected OutputStream getOutputStream( final String key )
311 throws IOException
312 {
313 return new FileOutputStream( getFile( key ) );
314 }
315
316
317
318
319
320
321 public synchronized void remove( final String key )
322 {
323 try
324 {
325 final File file = getFile( key );
326 file.delete();
327 if( DEBUG ) getLogger().debug( "removed key " + key );
328 }
329 catch( final Exception e )
330 {
331 throw new RuntimeException( "Exception caught while removing" +
332 " an object: " + e );
333 }
334 }
335
336
337
338
339
340
341
342
343 public synchronized boolean containsKey( final String key )
344 {
345 try
346 {
347 final File file = getFile( key );
348 if( DEBUG ) getLogger().debug( "checking key " + key );
349 return file.exists();
350 }
351 catch( final Exception e )
352 {
353 throw new RuntimeException( "Exception caught while searching " +
354 "an object: " + e );
355 }
356 }
357
358
359
360
361 public Iterator list()
362 {
363 final File storeDir = new File( m_path );
364 final String[] names = storeDir.list( m_filter );
365 final ArrayList list = new ArrayList();
366
367 for( int i = 0; i < names.length; i++ )
368 {
369 String decoded = decode(names[i]);
370 list.add(decoded);
371 }
372
373 return list.iterator();
374 }
375
376
377
378
379
380
381
382
383
384
385
386
387
388 protected String encode( final String key )
389 {
390 final byte[] bytes = key.getBytes();
391 final char[] buffer = new char[ bytes.length << 1 ];
392
393 for( int i = 0, j = 0; i < bytes.length; i++ )
394 {
395 final int k = bytes[ i ];
396 buffer[ j++ ] = HEX_DIGITS[ ( k >>> 4 ) & BYTE_MASK ];
397 buffer[ j++ ] = HEX_DIGITS[ k & BYTE_MASK ];
398 }
399
400 StringBuffer result = new StringBuffer();
401 result.append( m_path );
402 result.append( File.separator );
403 result.append( buffer );
404 result.append( m_extension );
405 return result.toString();
406 }
407
408
409
410
411
412
413
414
415
416
417
418 protected String decode( String filename )
419 {
420 filename = filename.substring( 0, filename.length() - m_extension.length() );
421 final int size = filename.length();
422 final byte[] bytes = new byte[ size >>> 1 ];
423
424 for( int i = 0, j = 0; i < size; j++ )
425 {
426 bytes[ j ] = Byte.parseByte( filename.substring( i, i + 2 ), 16 );
427 i +=2;
428 }
429
430 return new String( bytes );
431 }
432 }