View Javadoc
1   /*
2    *  jDTAUS Banking RI DTAUS
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.banking.dtaus.ri.zka;
22  
23  import java.io.EOFException;
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.RandomAccessFile;
27  import java.util.Iterator;
28  import java.util.Locale;
29  import java.util.Map;
30  import org.jdtaus.banking.dtaus.CorruptedException;
31  import org.jdtaus.banking.dtaus.PhysicalFile;
32  import org.jdtaus.banking.dtaus.PhysicalFileException;
33  import org.jdtaus.banking.dtaus.PhysicalFileFactory;
34  import org.jdtaus.banking.dtaus.spi.Fields;
35  import org.jdtaus.banking.messages.IllegalDataMessage;
36  import org.jdtaus.banking.messages.IllegalFileLengthMessage;
37  import org.jdtaus.core.container.ContainerFactory;
38  import org.jdtaus.core.container.Implementation;
39  import org.jdtaus.core.container.ModelFactory;
40  import org.jdtaus.core.container.PropertyException;
41  import org.jdtaus.core.io.FileOperations;
42  import org.jdtaus.core.io.util.CoalescingFileOperations;
43  import org.jdtaus.core.io.util.RandomAccessFileOperations;
44  import org.jdtaus.core.io.util.ReadAheadFileOperations;
45  import org.jdtaus.core.nio.util.Charsets;
46  import org.jdtaus.core.text.Message;
47  
48  /**
49   * Default {@code PhysicalFileFactory} implementation.
50   *
51   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
52   * @version $JDTAUS: DefaultPhysicalFileFactory.java 8865 2014-01-10 17:13:42Z schulte $
53   */
54  public final class DefaultPhysicalFileFactory implements PhysicalFileFactory
55  {
56  
57      /**
58       * Constant for the name of attribute {@code readAheadCaching}.
59       * <p>
60       * The {@code readAheadCaching} attribute is used to enabled or disable the
61       * use of a read-ahead caching algorithm. Its expected value is of type
62       * {@code Boolean}.
63       * </p>
64       */
65      public static final String ATTRIBUTE_READAHEAD_CACHING =
66          DefaultPhysicalFileFactory.class.getName() + ".readAheadCaching";
67  
68      /**
69       * Constant for the name of attribute {@code readAheadCacheSize}.
70       * <p>
71       * The {@code readAheadCacheSize} attribute is used to specify the
72       * size of the read-ahead cache. Its expected value is of type
73       * {@code Integer}.
74       * </p>
75       */
76      public static final String ATTRIBUTE_READAHEAD_CACHESIZE =
77          DefaultPhysicalFileFactory.class.getName() + ".readAheadCacheSize";
78  
79      /**
80       * Constant for the name of attribute {@code coalescingCaching}.
81       * <p>
82       * The {@code coalescingCaching} attribute is used to enabled or disable the
83       * use of a coalescing caching algorithm. Its expected value is of type
84       * {@code Boolean}.
85       * </p>
86       */
87      public static final String ATTRIBUTE_COALESCING_CACHING =
88          DefaultPhysicalFileFactory.class.getName() + ".coalescingCaching";
89  
90      /**
91       * Constant for the name of attribute {@code coalescingBlockSize}.
92       * <p>
93       * The {@code coalescingBlockSize} attribute is used to specify the
94       * value of property {@code blockSize} to use when constructing the
95       * coalescing cache implementation. Its expected value is of type
96       * {@code Integer}.
97       * </p>
98       */
99      public static final String ATTRIBUTE_COALESCING_BLOCKSIZE =
100         DefaultPhysicalFileFactory.class.getName() + ".coalescingBlockSize";
101 
102     /**
103      * Constant for the name of attribute {@code spaceCharactersAllowed}.
104      * <p>
105      * The {@code spaceCharactersAllowed} attribute is used to specify numeric
106      * fields for which space characters are to be allowed. It is used as
107      * a prefix with the hexadecimal field constant appended. Its expected value
108      * is of type {@code Boolean}.
109      * </p>
110      */
111     public static final String ATTRIBUTE_SPACE_CHARACTERS_ALLOWED =
112         DefaultPhysicalFileFactory.class.getName() + ".spaceCharactersAllowed.";
113 
114     /** Implementation meta-data. */
115     private Implementation implementation;
116 
117     public int analyse( final File file ) throws PhysicalFileException, IOException
118     {
119         if ( file == null )
120         {
121             throw new NullPointerException( "file" );
122         }
123 
124         this.assertValidProperties();
125         final FileOperations ops = new RandomAccessFileOperations( new RandomAccessFile( file, "r" ) );
126         final int format = this.analyse( ops );
127         ops.close();
128         return format;
129     }
130 
131     public int analyse( final FileOperations fileOperations ) throws PhysicalFileException, IOException
132     {
133         int blockSize = 128;
134         long remainder = 0;
135         int read = 0;
136         int ret = FORMAT_DISK;
137         int total = 0;
138 
139         final Message[] messages;
140         final byte[] buf = new byte[ 4 ];
141         final String str;
142         final long length;
143 
144         if ( fileOperations == null )
145         {
146             throw new NullPointerException( "fileOperations" );
147         }
148 
149         this.assertValidProperties();
150         length = fileOperations.getLength();
151         try
152         {
153             ThreadLocalMessages.getMessages().clear();
154             ThreadLocalMessages.setErrorsEnabled( false );
155 
156             if ( length >= 128 )
157             { // mindestens ein Disketten-Satzabschnitt.
158                 // die ersten 4 Byte lesen.
159                 fileOperations.setFilePointer( 0L );
160                 do
161                 {
162                     read = fileOperations.read( buf, total, buf.length - total );
163                     if ( read == FileOperations.EOF )
164                     {
165                         throw new EOFException();
166                     }
167                     else
168                     {
169                         total += read;
170                     }
171                 }
172                 while ( total < buf.length );
173 
174                 // Diskettenformat prüfen "0128".
175                 str = Charsets.decode( buf, "ISO646-DE" );
176                 if ( "0128".equals( str ) )
177                 {
178                     remainder = length % blockSize;
179                 }
180                 else
181                 {
182                     final int size = ( ( buf[0] & 0xFF ) << 8 ) | ( buf[1] & 0xFF );
183                     if ( size == 150 )
184                     {
185                         ret = FORMAT_TAPE;
186                         blockSize = 150;
187                         remainder = 0; // Variable blocksize.
188                     }
189                     else
190                     {
191                         if ( ThreadLocalMessages.isErrorsEnabled() )
192                         {
193                             throw new CorruptedException( this.getImplementation(), 0L );
194                         }
195                         else
196                         {
197                             final Message msg = new IllegalDataMessage(
198                                 Fields.FIELD_A1, IllegalDataMessage.TYPE_CONSTANT, 0L, str );
199 
200                             ThreadLocalMessages.getMessages().addMessage( msg );
201                         }
202                     }
203                 }
204             }
205             else
206             {
207                 if ( ThreadLocalMessages.isErrorsEnabled() )
208                 {
209                     throw new CorruptedException( this.getImplementation(), length );
210                 }
211                 else
212                 {
213                     final Message msg = new IllegalFileLengthMessage( length, blockSize );
214                     ThreadLocalMessages.getMessages().addMessage( msg );
215                 }
216             }
217 
218             if ( remainder > 0 )
219             {
220                 if ( ThreadLocalMessages.isErrorsEnabled() )
221                 {
222                     throw new CorruptedException( this.getImplementation(), length );
223                 }
224                 else
225                 {
226                     final Message msg = new IllegalFileLengthMessage( length, blockSize );
227                     ThreadLocalMessages.getMessages().addMessage( msg );
228                 }
229             }
230 
231             messages = ThreadLocalMessages.getMessages().getMessages();
232             if ( messages.length > 0 )
233             {
234                 throw new PhysicalFileException( messages );
235             }
236 
237             return ret;
238         }
239         finally
240         {
241             ThreadLocalMessages.setErrorsEnabled( true );
242         }
243     }
244 
245     public PhysicalFile createPhysicalFile( final File file, final int format )
246         throws IOException
247     {
248         return this.createPhysicalFile( file, format, this.getDefaultProperties() );
249     }
250 
251     public PhysicalFile createPhysicalFile( final File file, final int format, final java.util.Properties properties )
252         throws IOException
253     {
254         if ( file == null )
255         {
256             throw new NullPointerException( "file" );
257         }
258 
259         this.assertValidProperties();
260         this.assertValidProperties( properties );
261 
262         FileOperations ops = new RandomAccessFileOperations( new RandomAccessFile( file, "rw" ) );
263         ops = this.configureCoalescingCaching( ops, properties );
264         return this.createPhysicalFile( ops, format, properties );
265     }
266 
267     public PhysicalFile createPhysicalFile(
268         final FileOperations ops, final int format ) throws IOException
269     {
270         return this.createPhysicalFile( ops, format,
271                                         this.getDefaultProperties() );
272 
273     }
274 
275     public PhysicalFile createPhysicalFile( FileOperations ops, final int format, final java.util.Properties properties )
276         throws IOException
277     {
278         if ( ops == null )
279         {
280             throw new NullPointerException( "ops" );
281         }
282         if ( format != FORMAT_DISK && format != FORMAT_TAPE )
283         {
284             throw new IllegalArgumentException( Integer.toString( format ) );
285         }
286 
287         this.assertValidProperties();
288         this.assertValidProperties( properties );
289 
290         try
291         {
292             ops.setLength( 0L );
293             return this.getPhysicalFile( ops, format, properties );
294         }
295         catch ( PhysicalFileException e )
296         {
297             throw new AssertionError( e );
298         }
299     }
300 
301     public PhysicalFile getPhysicalFile( final File file ) throws PhysicalFileException, IOException
302     {
303         return this.getPhysicalFile( file, this.getDefaultProperties() );
304     }
305 
306     public PhysicalFile getPhysicalFile( final FileOperations ops ) throws PhysicalFileException, IOException
307     {
308         return this.getPhysicalFile( ops, this.getDefaultProperties() );
309     }
310 
311     public PhysicalFile getPhysicalFile( final FileOperations ops, final java.util.Properties properties )
312         throws PhysicalFileException, IOException
313     {
314         if ( ops == null )
315         {
316             throw new NullPointerException( "ops" );
317         }
318 
319         this.assertValidProperties();
320         this.assertValidProperties( properties );
321         return this.getPhysicalFile( ops, this.getDefaultFormat(), properties );
322     }
323 
324     public PhysicalFile getPhysicalFile( final File file, final java.util.Properties properties )
325         throws PhysicalFileException, IOException
326     {
327         if ( file == null )
328         {
329             throw new NullPointerException( "file" );
330         }
331 
332         this.assertValidProperties();
333         this.assertValidProperties( properties );
334 
335         FileOperations ops = new RandomAccessFileOperations( new RandomAccessFile( file, "rw" ) );
336         ops = this.configureReadAheadCaching( ops, properties );
337         return this.getPhysicalFile( ops, properties );
338     }
339 
340     /**
341      * Checks configured properties.
342      *
343      * @throws PropertyException for illegal property values.
344      */
345     private void assertValidProperties()
346     {
347         final int defaultFormat = this.getDefaultFormat();
348         if ( defaultFormat != FORMAT_DISK && defaultFormat != FORMAT_TAPE )
349         {
350             throw new PropertyException( "defaultFormat", new Integer( defaultFormat ) );
351         }
352     }
353 
354     /**
355      * Checks that given properties are valid.
356      *
357      * @param properties the properties to check.
358      *
359      * @throws NullPointerException if {@code properties} is {@code null}.
360      * @throws IllegalArgumentException if {@code properties} holds invalid values.
361      */
362     private void assertValidProperties( final java.util.Properties properties )
363     {
364         if ( properties == null )
365         {
366             throw new NullPointerException( "properties" );
367         }
368 
369         for ( Iterator it = properties.entrySet().iterator(); it.hasNext(); )
370         {
371             final Map.Entry entry = (Map.Entry) it.next();
372             final String name = (String) entry.getKey();
373             final String value = (String) entry.getValue();
374 
375             if ( name.startsWith( ATTRIBUTE_SPACE_CHARACTERS_ALLOWED ) )
376             {
377                 try
378                 {
379                     Integer.parseInt( name.substring( name.lastIndexOf( '.' ) + 1 ), 16 );
380                 }
381                 catch ( NumberFormatException e )
382                 {
383                     throw (IllegalArgumentException) new IllegalArgumentException(
384                         name + ": " + e.getMessage() ).initCause( e );
385 
386                 }
387             }
388 
389             if ( value != null && ( ATTRIBUTE_READAHEAD_CACHESIZE.equals( name ) ||
390                                     ATTRIBUTE_COALESCING_BLOCKSIZE.equals( name ) ) )
391             {
392                 try
393                 {
394                     Integer.parseInt( value );
395                 }
396                 catch ( NumberFormatException e )
397                 {
398                     throw (IllegalArgumentException) new IllegalArgumentException( this.getIllegalAttributeTypeMessage(
399                         this.getLocale(), name, ( value != null ? value.getClass().getName() : null ),
400                         Integer.class.getName() ) ).initCause( e );
401 
402                 }
403             }
404         }
405     }
406 
407     private java.util.Properties getDefaultProperties()
408     {
409         final java.util.Properties properties = new java.util.Properties();
410         properties.setProperty( ATTRIBUTE_READAHEAD_CACHING, Boolean.toString( true ) );
411         properties.setProperty( ATTRIBUTE_COALESCING_CACHING, Boolean.toString( true ) );
412         return properties;
413     }
414 
415     private FileOperations configureReadAheadCaching( FileOperations ops, final java.util.Properties properties )
416         throws IOException
417     {
418         final String readAheadCaching = properties.getProperty( ATTRIBUTE_READAHEAD_CACHING );
419         final String readAheadCacheSize = properties.getProperty( ATTRIBUTE_READAHEAD_CACHESIZE );
420         final boolean isReadAheadCaching =
421             readAheadCaching != null && Boolean.valueOf( readAheadCaching ).booleanValue();
422 
423         if ( isReadAheadCaching )
424         {
425             if ( readAheadCacheSize != null )
426             {
427                 ops = new ReadAheadFileOperations( ops, Integer.parseInt( readAheadCacheSize ) );
428             }
429             else
430             {
431                 ops = new ReadAheadFileOperations( ops );
432             }
433         }
434 
435         return ops;
436     }
437 
438     private FileOperations configureCoalescingCaching( FileOperations ops, final java.util.Properties properties )
439         throws IOException
440     {
441         final String coalescingCaching = properties.getProperty( ATTRIBUTE_COALESCING_CACHING );
442         final String coalescingBlockSize = properties.getProperty( ATTRIBUTE_COALESCING_BLOCKSIZE );
443         final boolean isCoalescingCaching =
444             coalescingCaching != null && Boolean.valueOf( coalescingCaching ).booleanValue();
445 
446         if ( isCoalescingCaching )
447         {
448             if ( coalescingBlockSize != null )
449             {
450                 ops = new CoalescingFileOperations( ops, Integer.parseInt( coalescingBlockSize ) );
451             }
452             else
453             {
454                 ops = new CoalescingFileOperations( ops );
455             }
456         }
457 
458         return ops;
459     }
460 
461     private PhysicalFile getPhysicalFile( final FileOperations ops, int format, final java.util.Properties properties )
462         throws PhysicalFileException, IOException
463     {
464         if ( ops == null )
465         {
466             throw new NullPointerException( "ops" );
467         }
468         if ( format != FORMAT_DISK && format != FORMAT_TAPE )
469         {
470             throw new IllegalArgumentException( Integer.toString( format ) );
471         }
472 
473         this.assertValidProperties( properties );
474 
475         final DefaultPhysicalFile ret;
476         final Message[] messages;
477         format = ops.getLength() > 0 ? this.analyse( ops ) : format;
478 
479         try
480         {
481             ThreadLocalMessages.getMessages().clear();
482             ThreadLocalMessages.setErrorsEnabled( false );
483             ret = new DefaultPhysicalFile( format, ops, properties );
484             messages = ThreadLocalMessages.getMessages().getMessages();
485             if ( messages.length > 0 )
486             {
487                 throw new PhysicalFileException( messages );
488             }
489 
490             return ret;
491         }
492         finally
493         {
494             ThreadLocalMessages.setErrorsEnabled( true );
495         }
496     }
497 
498     protected Implementation getImplementation()
499     {
500         if ( this.implementation == null )
501         {
502             this.implementation = ModelFactory.getModel().getModules().
503                 getImplementation( DefaultPhysicalFileFactory.class.getName() );
504 
505         }
506 
507         return this.implementation;
508     }
509 
510     //--Constructors------------------------------------------------------------
511 
512 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors
513     // This section is managed by jdtaus-container-mojo.
514 
515     /** Standard implementation constructor <code>org.jdtaus.banking.dtaus.ri.zka.DefaultPhysicalFileFactory</code>. */
516     public DefaultPhysicalFileFactory()
517     {
518         super();
519     }
520 
521 // </editor-fold>//GEN-END:jdtausConstructors
522 
523     //------------------------------------------------------------Constructors--
524     //--Properties--------------------------------------------------------------
525 
526 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
527     // This section is managed by jdtaus-container-mojo.
528 
529     /**
530      * Gets the value of property <code>defaultFormat</code>.
531      *
532      * @return The format to use for empty files.
533      */
534     private int getDefaultFormat()
535     {
536         return ( (java.lang.Integer) ContainerFactory.getContainer().
537             getProperty( this, "defaultFormat" ) ).intValue();
538 
539     }
540 
541 // </editor-fold>//GEN-END:jdtausProperties
542 
543     //--------------------------------------------------------------Properties--
544     //--Dependencies------------------------------------------------------------
545 
546 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
547     // This section is managed by jdtaus-container-mojo.
548 
549     /**
550      * Gets the configured <code>Locale</code> implementation.
551      *
552      * @return The configured <code>Locale</code> implementation.
553      */
554     private Locale getLocale()
555     {
556         return (Locale) ContainerFactory.getContainer().
557             getDependency( this, "Locale" );
558 
559     }
560 
561 // </editor-fold>//GEN-END:jdtausDependencies
562 
563     //------------------------------------------------------------Dependencies--
564     //--Messages----------------------------------------------------------------
565 
566 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages
567     // This section is managed by jdtaus-container-mojo.
568 
569     /**
570      * Gets the text of message <code>illegalAttributeType</code>.
571      * <blockquote><pre>Ungültiger Attribut-Typ {1} für Attribut {0}. Erwartet Typ {2}.</pre></blockquote>
572      * <blockquote><pre>The type {1} for attribute {0} is invalid. Expected {2}.</pre></blockquote>
573      *
574      * @param locale The locale of the message instance to return.
575      * @param attributeName format parameter.
576      * @param typeName format parameter.
577      * @param expectedTypeName format parameter.
578      *
579      * @return the text of message <code>illegalAttributeType</code>.
580      */
581     private String getIllegalAttributeTypeMessage( final Locale locale,
582             final java.lang.String attributeName,
583             final java.lang.String typeName,
584             final java.lang.String expectedTypeName )
585     {
586         return ContainerFactory.getContainer().
587             getMessage( this, "illegalAttributeType", locale,
588                 new Object[]
589                 {
590                     attributeName,
591                     typeName,
592                     expectedTypeName
593                 });
594 
595     }
596 
597 // </editor-fold>//GEN-END:jdtausMessages
598 
599     //----------------------------------------------------------------Messages--
600 }