Changeset 5291


Ignore:
Timestamp:
Aug 29, 2016, 7:31:15 PM (6 years ago)
Author:
Christian Schulte
Message:

o Updated to add methods 'getExecutorService' and 'setExecutorService' to class

'SectionEditor?' and to make that class support editing sections in parallel.

Closes #106

Location:
jomc-util/trunk/src
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • jomc-util/trunk/src/main/java/org/jomc/util/SectionEditor.java

    r5091 r5291  
    3232
    3333import java.io.IOException;
     34import java.lang.reflect.UndeclaredThrowableException;
    3435import java.text.MessageFormat;
    35 import java.util.HashMap;
     36import java.util.Collection;
     37import java.util.LinkedList;
     38import java.util.List;
    3639import java.util.Map;
    3740import java.util.ResourceBundle;
    3841import java.util.Stack;
     42import java.util.concurrent.Callable;
     43import java.util.concurrent.CancellationException;
     44import java.util.concurrent.ConcurrentHashMap;
     45import java.util.concurrent.ExecutionException;
     46import java.util.concurrent.ExecutorService;
     47import java.util.concurrent.Future;
    3948
    4049/**
     
    7382     * Mapping of section names to flags indicating presence of the section.
    7483     */
    75     private final Map<String, Boolean> presenceFlags = new HashMap<String, Boolean>();
     84    private final Map<String, Boolean> presenceFlags = new ConcurrentHashMap<String, Boolean>( 32 );
     85
     86    /**
     87     * The {@code ExecutorService} of the instance.
     88     *
     89     * @since 1.10
     90     */
     91    private ExecutorService executorService;
    7692
    7793    /**
     
    112128    {
    113129        super( editor, lineSeparator );
     130    }
     131
     132    /**
     133     * Gets an {@code ExecutorService} used to edit sections in parallel.
     134     *
     135     * @return An {@code ExecutorService} used to edit sections in parallel or {@code null}, if no such service has
     136     * been provided by an application.
     137     *
     138     * @since 1.10
     139     *
     140     * @see #setExecutorService(java.util.concurrent.ExecutorService)
     141     */
     142    public final ExecutorService getExecutorService()
     143    {
     144        return this.executorService;
     145    }
     146
     147    /**
     148     * Sets the {@code ExecutorService} to be used to edit sections in parallel.
     149     * <p>
     150     * The {@code ExecutorService} to be used to edit sections in parallel is an optional entity. If no such service is
     151     * provided by an application, no parallelization is performed. Configuration or lifecycle management of the given
     152     * {@code ExecutorService} is the responsibility of the application.
     153     * </p>
     154     *
     155     * @param value The {@code ExecutorService} to be used to edit sections in parallel or {@code null}, to disable any
     156     * parallelization.
     157     *
     158     * @since 1.10
     159     *
     160     * @see #getExecutorService()
     161     */
     162    public final void setExecutorService( final ExecutorService value )
     163    {
     164        this.executorService = value;
    114165    }
    115166
     
    250301    protected boolean isSectionFinished( final String line ) throws IOException
    251302    {
    252         return line != null && line.indexOf( DEFAULT_SECTION_END ) != -1;
     303        return line != null && line.contains( DEFAULT_SECTION_END );
    253304    }
    254305
     
    279330
    280331    /**
    281      * Edits a section recursively.
     332     * Creates tasks recursively for editing sections in parallel.
    282333     *
    283334     * @param section The section to edit recursively.
    284      *
    285      * @throws NullPointerException if {@code section} is {@code null}.
     335     * @param tasks The collection of tasks to run in parallel.
     336     *
     337     * @throws NullPointerException if {@code section} or {@code tasks} is {@code null}.
    286338     * @throws IOException if editing fails.
    287339     */
    288     private void editSections( final Section section ) throws IOException
     340    private void editSections( final Section section, final Collection<EditSectionTask> tasks ) throws IOException
    289341    {
    290342        if ( section == null )
     
    292344            throw new NullPointerException( "section" );
    293345        }
    294 
    295         this.editSection( section );
     346        if ( tasks == null )
     347        {
     348            throw new NullPointerException( "tasks" );
     349        }
     350
     351        tasks.add( new EditSectionTask( section ) );
    296352        for ( int i = 0, s0 = section.getSections().size(); i < s0; i++ )
    297353        {
    298             this.editSections( section.getSections().get( i ) );
     354            this.editSections( section.getSections().get( i ), tasks );
    299355        }
    300356    }
     
    321377        }
    322378
    323         this.presenceFlags.clear();
    324         this.editSections( section );
    325         return this.renderSections( section, new StringBuilder( 512 ) ).toString();
     379        try
     380        {
     381            this.presenceFlags.clear();
     382            final List<EditSectionTask> tasks = new LinkedList<EditSectionTask>();
     383            this.editSections( section, tasks );
     384
     385            if ( this.getExecutorService() != null && tasks.size() > 1 )
     386            {
     387                for ( final Future<Void> task : this.getExecutorService().invokeAll( tasks ) )
     388                {
     389                    task.get();
     390                }
     391            }
     392            else
     393            {
     394                for ( int i = 0, s0 = tasks.size(); i < s0; i++ )
     395                {
     396                    tasks.get( i ).call();
     397                }
     398            }
     399
     400            return this.renderSections( section, new StringBuilder( 512 ) ).toString();
     401        }
     402        catch ( final CancellationException e )
     403        {
     404            throw (IOException) new IOException( getMessage( e ) ).initCause( e );
     405        }
     406        catch ( final InterruptedException e )
     407        {
     408            throw (IOException) new IOException( getMessage( e ) ).initCause( e );
     409        }
     410        catch ( final ExecutionException e )
     411        {
     412            if ( e.getCause() instanceof IOException )
     413            {
     414                throw (IOException) e.getCause();
     415            }
     416            else if ( e.getCause() instanceof RuntimeException )
     417            {
     418                // The fork-join framework breaks the exception handling contract of Callable by re-throwing any
     419                // exception caught using a runtime exception.
     420                if ( e.getCause().getCause() instanceof IOException )
     421                {
     422                    throw (IOException) e.getCause().getCause();
     423                }
     424                else if ( e.getCause().getCause() instanceof RuntimeException )
     425                {
     426                    throw (RuntimeException) e.getCause().getCause();
     427                }
     428                else if ( e.getCause().getCause() instanceof Error )
     429                {
     430                    throw (Error) e.getCause().getCause();
     431                }
     432                else if ( e.getCause().getCause() instanceof Exception )
     433                {
     434                    // Checked exception not declared to be thrown by the Callable's 'call' method.
     435                    throw new UndeclaredThrowableException( e.getCause().getCause() );
     436                }
     437                else
     438                {
     439                    throw (RuntimeException) e.getCause();
     440                }
     441            }
     442            else if ( e.getCause() instanceof Error )
     443            {
     444                throw (Error) e.getCause();
     445            }
     446            else
     447            {
     448                // Checked exception not declared to be thrown by the Callable's 'call' method.
     449                throw new UndeclaredThrowableException( e.getCause() );
     450            }
     451        }
    326452    }
    327453
     
    337463    {
    338464        return sectionName != null && this.presenceFlags.get( sectionName ) != null
    339                    && this.presenceFlags.get( sectionName ).booleanValue();
     465                   && this.presenceFlags.get( sectionName );
    340466
    341467    }
     
    373499    }
    374500
     501    private final class EditSectionTask implements Callable<Void>
     502    {
     503
     504        private final Section section;
     505
     506        EditSectionTask( final Section section )
     507        {
     508            super();
     509            this.section = section;
     510        }
     511
     512        public Void call() throws IOException
     513        {
     514            editSection( this.section );
     515            return null;
     516        }
     517
     518    }
     519
    375520    private static String getMessage( final String key, final Object... arguments )
    376521    {
    377         return MessageFormat.format( ResourceBundle.getBundle( SectionEditor.class.getName().
    378             replace( '.', '/' ) ).getString( key ), arguments );
     522        return MessageFormat.format( ResourceBundle.getBundle( SectionEditor.class.getName() ).getString( key ),
     523                                     arguments );
     524
     525    }
     526
     527    private static String getMessage( final Throwable t )
     528    {
     529        return t != null
     530                   ? t.getMessage() != null && t.getMessage().trim().length() > 0
     531                         ? t.getMessage()
     532                         : getMessage( t.getCause() )
     533                   : null;
    379534
    380535    }
  • jomc-util/trunk/src/test/java/org/jomc/util/test/SectionEditorTest.java

    r5091 r5291  
    3535import java.io.InputStream;
    3636import java.io.StringReader;
     37import java.util.concurrent.ExecutorService;
    3738import org.apache.commons.io.IOUtils;
    3839import org.jomc.util.LineEditor;
    3940import org.jomc.util.Section;
    4041import org.jomc.util.SectionEditor;
     42import org.junit.After;
    4143import org.junit.Test;
    4244import static org.junit.Assert.assertEquals;
     
    6163
    6264    /**
     65     * The {@code ExecutorService} backing the tests.
     66     */
     67    private ExecutorService executorService;
     68
     69    /**
    6370     * Creates a new {@code SectionEditorTest} instance.
    6471     */
     
    8390    protected SectionEditor newLineEditor()
    8491    {
    85         return new SectionEditor()
     92        final SectionEditor sectionEditor = new SectionEditor()
    8693        {
    8794
     
    103110
    104111        };
     112
     113        sectionEditor.setExecutorService( this.getExecutorService() );
     114        return sectionEditor;
    105115    }
    106116
     
    111121    protected SectionEditor newLineEditor( final LineEditor editor )
    112122    {
    113         return new SectionEditor( editor );
     123        final SectionEditor sectionEditor = new SectionEditor( editor );
     124        sectionEditor.setExecutorService( this.getExecutorService() );
     125        return sectionEditor;
     126    }
     127
     128    /**
     129     * Gets the {@code ExecutorService} backing the tests.
     130     *
     131     * @return The {@code ExecutorService} backing the tests.
     132     *
     133     * @see #newExecutorService()
     134     * @since 1.10
     135     */
     136    public final ExecutorService getExecutorService()
     137    {
     138        if ( this.executorService == null )
     139        {
     140            this.executorService = this.newExecutorService();
     141        }
     142
     143        return this.executorService;
     144    }
     145
     146    /**
     147     * Creates a new {@code ExecutorService} backing the tests.
     148     *
     149     * @return A new {@code ExecutorService} backing the tests, or {@code null}.
     150     *
     151     * @see #getExecutorService()
     152     * @since 1.10
     153     */
     154    protected ExecutorService newExecutorService()
     155    {
     156        return null;
     157    }
     158
     159    /**
     160     * Shuts down the {@code ExecutorService} backing the tests, if not {@code null}.
     161     */
     162    @After
     163    public final void shutdown()
     164    {
     165        if ( this.executorService != null )
     166        {
     167            this.executorService.shutdown();
     168            this.executorService = null;
     169        }
    114170    }
    115171
Note: See TracChangeset for help on using the changeset viewer.