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.IOException;
24  import java.util.Iterator;
25  import java.util.Map;
26  import org.jdtaus.banking.dtaus.Checksum;
27  import org.jdtaus.banking.dtaus.Header;
28  import org.jdtaus.banking.dtaus.LogicalFile;
29  import org.jdtaus.banking.dtaus.PhysicalFile;
30  import org.jdtaus.banking.dtaus.PhysicalFileFactory;
31  import org.jdtaus.banking.dtaus.spi.HeaderValidator;
32  import org.jdtaus.banking.dtaus.spi.IllegalHeaderException;
33  import org.jdtaus.banking.messages.AnalysesFileMessage;
34  import org.jdtaus.core.container.ContainerFactory;
35  import org.jdtaus.core.io.FileOperations;
36  import org.jdtaus.core.monitor.spi.Task;
37  import org.jdtaus.core.monitor.spi.TaskMonitor;
38  
39  /**
40   * Default {@code PhysicalFile} implementation.
41   * <p><b>Note:</b><br/>
42   * This implementation is not thread-safe.</p>
43   *
44   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
45   * @version $JDTAUS: DefaultPhysicalFile.java 8661 2012-09-27 11:29:58Z schulte $
46   */
47  public final class DefaultPhysicalFile implements PhysicalFile
48  {
49  
50      /** Index der logischen Dateien. */
51      private AbstractLogicalFile[] index;
52  
53      /** Anzahl vorhandener logischer Dateien. */
54      private int dtausCount = 0;
55  
56      /** Mapping of attribute names to theire values. */
57      private final java.util.Properties properties;
58  
59      /** <code>FileOperations</code> requirement. **/
60      private FileOperations fileOperations;
61  
62      /** Format of this instance. */
63      private final int format;
64  
65      /**
66       * Creates a new {@code DefaultPhysicalFile} instance.
67       *
68       * @param format The format of the new instance.
69       * @param fileOperations The {@code FileOperations} implementation to operate on.
70       * @param properties Configuration properties.
71       *
72       * @throws NullPointerException if either {@code fileOperations} or {@code properties} is {@code null}.
73       * @throws IllegalArgumentException if {@code format} is not equal to {@code FORMAT_DISK} and {@code FORMAT_TAPE}.
74       * @throws IOException wenn nicht gelesen werden kann.
75       *
76       * @see PhysicalFileFactory#FORMAT_DISK
77       * @see PhysicalFileFactory#FORMAT_TAPE
78       */
79      public DefaultPhysicalFile(
80          final int format, final FileOperations fileOperations, final java.util.Properties properties )
81          throws IOException
82      {
83          super();
84  
85          if ( fileOperations == null )
86          {
87              throw new NullPointerException( "fileOperations" );
88          }
89          if ( properties == null )
90          {
91              throw new NullPointerException( "properties" );
92          }
93          if ( format != PhysicalFileFactory.FORMAT_DISK && format != PhysicalFileFactory.FORMAT_TAPE )
94          {
95              throw new IllegalArgumentException( Integer.toString( format ) );
96          }
97  
98          this.properties = properties;
99          this.fileOperations = fileOperations;
100         this.format = format;
101         this.checksum();
102     }
103 
104     public int count()
105     {
106         return this.dtausCount;
107     }
108 
109     public LogicalFile add( final Header header ) throws IOException
110     {
111         if ( header == null )
112         {
113             throw new NullPointerException( "header" );
114         }
115 
116         IllegalHeaderException result = null;
117         final HeaderValidator[] validators = this.getHeaderValidator();
118 
119         for ( int i = validators.length - 1; i >= 0; i-- )
120         {
121             result = validators[i].assertValidHeader( header, result );
122         }
123 
124         if ( result != null && result.getMessages().length > 0 )
125         {
126             throw result;
127         }
128 
129         this.resizeIndex( this.dtausCount );
130 
131         final AbstractLogicalFile lFile = this.newLogicalFile(
132             ( this.dtausCount == 0 ? 0L : this.index[this.dtausCount - 1].getChecksumPosition() +
133                                           this.index[this.dtausCount - 1].getBlockSize() ) );
134 
135         lFile.insertBytes( lFile.getHeaderPosition(), this.format * 2 );
136         lFile.writeHeader( header );
137         lFile.writeChecksum( new Checksum() );
138         lFile.checksum();
139         this.index[this.dtausCount] = lFile;
140         return this.index[this.dtausCount++];
141     }
142 
143     public LogicalFile get( int dtausId )
144     {
145         if ( !this.checkLogicalFileExists( dtausId ) )
146         {
147             throw new IllegalArgumentException( "dtausId" );
148         }
149         return this.index[dtausId];
150     }
151 
152     public void remove( int dtausId ) throws IOException
153     {
154         if ( !this.checkLogicalFileExists( dtausId ) )
155         {
156             throw new IllegalArgumentException( "dtausId" );
157         }
158 
159         this.index[dtausId].removeBytes(
160             this.index[dtausId].getHeaderPosition(), this.index[dtausId].getChecksumPosition() -
161                                                      this.index[dtausId].getHeaderPosition() + this.format );
162 
163         System.arraycopy( this.index, dtausId + 1, this.index, dtausId, --this.dtausCount - dtausId );
164     }
165 
166     public void commit() throws IOException
167     {
168         this.getFileOperations().close();
169     }
170 
171     public int getLogicalFileCount() throws IOException
172     {
173         return this.count();
174     }
175 
176     public LogicalFile addLogicalFile( final Header header ) throws IOException
177     {
178         return this.add( header );
179     }
180 
181     public LogicalFile getLogicalFile( final int index ) throws IOException
182     {
183         return this.get( index );
184     }
185 
186     public void removeLogicalFile( final int index ) throws IOException
187     {
188         this.remove( index );
189     }
190 
191     /** FileOperations requirement getter method. */
192     private FileOperations getFileOperations()
193     {
194         return this.fileOperations;
195     }
196 
197     private boolean checkLogicalFileExists( int dtausId )
198     {
199         return dtausId < this.dtausCount && dtausId >= 0;
200     }
201 
202     private void checksum() throws IOException
203     {
204         this.dtausCount = 0;
205         int dtausIndex = 0;
206         final long length = this.getFileOperations().getLength();
207         long maximumProgress = length;
208         long progressDivisor = 1L;
209 
210         while ( maximumProgress > Integer.MAX_VALUE )
211         {
212             maximumProgress /= 2L;
213             progressDivisor *= 2L;
214         }
215 
216         final Task task = new Task();
217         task.setIndeterminate( false );
218         task.setCancelable( false );
219         task.setDescription( new AnalysesFileMessage() );
220         task.setMinimum( 0 );
221         task.setProgress( 0 );
222         task.setMaximum( (int) maximumProgress );
223 
224         try
225         {
226             this.getTaskMonitor().monitor( task );
227 
228             for ( long position = 0L; position < length;
229                   position = this.index[dtausIndex].getChecksumPosition() + this.index[dtausIndex++].getBlockSize() )
230             {
231                 task.setProgress( (int) ( position / progressDivisor ) );
232                 this.resizeIndex( dtausIndex );
233                 this.index[dtausIndex] = this.newLogicalFile( position );
234                 this.index[dtausIndex].checksum();
235                 this.dtausCount++;
236             }
237         }
238         finally
239         {
240             this.getTaskMonitor().finish( task );
241         }
242     }
243 
244     private AbstractLogicalFile newLogicalFile( final long headerPosition ) throws IOException
245     {
246         final AbstractLogicalFile ret;
247 
248         switch ( this.format )
249         {
250             case PhysicalFileFactory.FORMAT_DISK:
251                 ret = new DTAUSDisk();
252                 break;
253             case PhysicalFileFactory.FORMAT_TAPE:
254                 ret = new DTAUSTape();
255                 break;
256             default:
257                 throw new IllegalStateException();
258 
259         }
260 
261         ret.setFileOperations( this.getFileOperations() );
262         ret.setHeaderPosition( headerPosition );
263         ret.setChecksumPosition( headerPosition + this.format );
264 
265         for ( Iterator it = this.properties.entrySet().iterator(); it.hasNext(); )
266         {
267             final Map.Entry e = (Map.Entry) it.next();
268             final String key = (String) e.getKey();
269 
270             if ( key.startsWith( DefaultPhysicalFileFactory.ATTRIBUTE_SPACE_CHARACTERS_ALLOWED ) )
271             {
272                 int field = Integer.parseInt( key.substring( key.lastIndexOf( '.' ) + 1 ), 16 );
273                 final boolean allowed =
274                     e.getValue() != null && Boolean.valueOf( e.getValue().toString() ).booleanValue();
275 
276                 ret.getConfiguration().setSpaceCharacterAllowed( field, allowed );
277             }
278         }
279 
280         ret.addListener( new AbstractLogicalFile.Listener()
281         {
282 
283             public void bytesInserted( final long position, final long bytes ) throws IOException
284             {
285                 final int fileIndex = this.getFileIndex( position );
286                 if ( fileIndex >= 0 )
287                 {
288                     // Increment properties headerPosition and checksumPosition for all remaining files.
289                     for ( int i = fileIndex + 1; i < dtausCount; i++ )
290                     {
291                         index[i].setHeaderPosition( index[i].getHeaderPosition() + bytes );
292                         index[i].setChecksumPosition( index[i].getChecksumPosition() + bytes );
293                     }
294                 }
295             }
296 
297             public void bytesDeleted( final long position, final long bytes ) throws IOException
298             {
299                 final int fileIndex = this.getFileIndex( position );
300                 if ( fileIndex >= 0 )
301                 {
302                     // Decrement properties headerPosition and checksumPosition for all remaining files.
303                     for ( int i = fileIndex + 1; i < dtausCount; i++ )
304                     {
305                         index[i].setHeaderPosition( index[i].getHeaderPosition() - bytes );
306                         index[i].setChecksumPosition( index[i].getChecksumPosition() - bytes );
307                     }
308                 }
309             }
310 
311             private int getFileIndex( final long position )
312             {
313                 for ( int i = dtausCount - 1; i >= 0; i-- )
314                 {
315                     if ( position >= index[i].getHeaderPosition() && position <= index[i].getChecksumPosition() )
316                     {
317                         return i;
318                     }
319                 }
320 
321                 return -1;
322             }
323 
324         } );
325 
326         return ret;
327     }
328 
329     private void resizeIndex( int index )
330     {
331         if ( this.index == null )
332         {
333             this.index = new AbstractLogicalFile[ index + 1 ];
334         }
335         else if ( this.index.length < index + 1 )
336         {
337             while ( this.index.length < index + 1 )
338             {
339                 final int newLength = this.index.length * 2;
340                 final AbstractLogicalFile[] newIndex = new AbstractLogicalFile[ newLength ];
341                 System.arraycopy( this.index, 0, newIndex, 0, this.index.length );
342                 this.index = newIndex;
343             }
344         }
345     }
346 
347     //--Dependencies------------------------------------------------------------
348 
349 // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
350     // This section is managed by jdtaus-container-mojo.
351 
352     /**
353      * Gets the configured <code>TaskMonitor</code> implementation.
354      *
355      * @return The configured <code>TaskMonitor</code> implementation.
356      */
357     private TaskMonitor getTaskMonitor()
358     {
359         return (TaskMonitor) ContainerFactory.getContainer().
360             getDependency( this, "TaskMonitor" );
361 
362     }
363 
364     /**
365      * Gets the configured <code>HeaderValidator</code> implementation.
366      *
367      * @return The configured <code>HeaderValidator</code> implementation.
368      */
369     private HeaderValidator[] getHeaderValidator()
370     {
371         return (HeaderValidator[]) ContainerFactory.getContainer().
372             getDependency( this, "HeaderValidator" );
373 
374     }
375 
376 // </editor-fold>//GEN-END:jdtausDependencies
377 
378     //------------------------------------------------------------Dependencies--
379 }