View Javadoc

1   /***
2    * Cecilia ADL Compiler
3    * Copyright (C) 2006-2008 STMicroelectronics
4    *
5    * This library is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU Lesser General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This library is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   * Lesser General Public License for more details.
14   *
15   * You should have received a copy of the GNU Lesser General Public
16   * License along with this library; if not, write to the Free Software
17   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   *
19   * Contact: fractal@objectweb.org
20   *
21   * Author:Matthieu Leclercq
22   */
23  
24  package org.objectweb.fractal.cecilia.adl.compiler;
25  
26  import java.io.File;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.concurrent.Callable;
34  import java.util.concurrent.ExecutionException;
35  import java.util.concurrent.ExecutorService;
36  import java.util.concurrent.Future;
37  import java.util.logging.Level;
38  import java.util.logging.Logger;
39  
40  import org.objectweb.fractal.adl.ADLException;
41  import org.objectweb.fractal.adl.util.FractalADLLogManager;
42  import org.objectweb.fractal.api.type.TypeFactory;
43  import org.objectweb.fractal.cecilia.adl.TypeNameProvider;
44  import org.objectweb.fractal.cecilia.adl.file.FutureFileCollectionProvider;
45  import org.objectweb.fractal.cecilia.adl.file.FutureFileProvider;
46  import org.objectweb.fractal.cecilia.adl.file.SourceFileWriter;
47  import org.objectweb.fractal.task.core.primitive.annotations.ClientInterface;
48  import org.objectweb.fractal.task.core.primitive.annotations.ServerInterface;
49  import org.objectweb.fractal.task.core.primitive.annotations.ServerInterfaces;
50  import org.objectweb.fractal.task.core.primitive.annotations.TaskParameters;
51  
52  @TaskParameters("id")
53  @ServerInterfaces(@ServerInterface(name = "file-provider", signature = FutureFileProvider.class, record = "role:archiveFile, id:%", parameters = "id"))
54  public abstract class AbstractArchiveTask implements FutureFileProvider {
55  
56    /**
57     * The suffix of the generated file. This suffix is used only if the output
58     * file name is retrieved using the
59     * {@link #typeNameProviderItf type name provider interface}.
60     */
61    public static final String                             FILE_NAME_SUFFIX           = ".a";
62  
63    /** The name of the linked file. */
64    protected final String                                 outputName;
65    /** The compilation flags. */
66    protected List<String>                                       flags;
67    /** The output directory. */
68    protected final File                                   outputDir;
69  
70    // Produced file
71    protected Future<File>                                 archiveFile;
72  
73    // The dep logger
74    protected static Logger                                depLogger                  = FractalADLLogManager
75                                                                                          .getLogger("dep");
76    // The io logger
77    protected static Logger                                ioLogger                   = FractalADLLogManager
78                                                                                          .getLogger("io");
79  
80    // ---------------------------------------------------------------------------
81    // Task client interfaces
82    // ---------------------------------------------------------------------------
83  
84    /** The name of the component type. */
85    @ClientInterface(name = "type-name-provider", contingency = TypeFactory.OPTIONAL, record = "role:typeNameProvider, id:%", parameters = "id")
86    public TypeNameProvider                                typeNameProviderItf;
87  
88    /** The client collection interface used to retrieve the files to linked. */
89    @ClientInterface(name = "source-file-provider", signature = FutureFileProvider.class, record = "role:compiledFile, id:%", parameters = "id")
90    public final Map<String, FutureFileProvider>           fileProviderItfs           = new HashMap<String, FutureFileProvider>();
91  
92    /** The client collection interface used to retrieve the files to linked. */
93    @ClientInterface(name = "source-file-collection-provider", signature = FutureFileCollectionProvider.class, record = "role:compiledFileCollection, id:%", parameters = "id")
94    public final Map<String, FutureFileCollectionProvider> fileCollectionProviderItfs = new HashMap<String, FutureFileCollectionProvider>();
95  
96    // ---------------------------------------------------------------------------
97    // Task constructor
98    // ---------------------------------------------------------------------------
99  
100   /**
101    * @param outputName the name of the linked file
102    * @param outputDir the directory into which the compiled file will be placed.
103    * @param flags compilation flags.
104    */
105   public AbstractArchiveTask(final String outputName, final File outputDir,
106       final List<String> flags) {
107     this.outputName = outputName;
108     this.outputDir = outputDir;
109     this.flags = flags;
110   }
111 
112   // ---------------------------------------------------------------------------
113   // Abstract methods
114   // ---------------------------------------------------------------------------
115 
116   /**
117    * Archive method.
118    * 
119    * @param outputFile the linked file that is provided by this task.
120    * @param inputFiles the input files to be linked.
121    * @throws the archive file. Most of the time this is the same the
122    *             <code>outputFile</code> parameter but it is not necessary the
123    *             case.
124    * @throws ADLException if an error occurs.
125    * @throws InterruptedException
126    */
127   protected abstract File archivate(File outputFile, Collection<File> inputFiles)
128       throws ADLException, InterruptedException;
129 
130   // ---------------------------------------------------------------------------
131   // Utility methods
132   // ---------------------------------------------------------------------------
133 
134   protected File getOutputFile() {
135     final String name;
136     if (outputName != null)
137       name = outputName;
138     else if (typeNameProviderItf != null)
139       name = typeNameProviderItf.getCTypeName() + FILE_NAME_SUFFIX;
140     else
141       throw new IllegalStateException(
142           "the output file name must either be specifyed in the contructor parameters or the typeNameProviderItf interface must be bound");
143 
144     return new File(outputDir, name);
145   }
146 
147   // -------------------------------------------------------------------------
148   // Implementation of the FileProvider interface
149   // -------------------------------------------------------------------------
150 
151   public synchronized Future<File> getFile(final ExecutorService executorService) {
152     if (archiveFile == null) {
153       final Collection<Future<File>> futureFiles = new HashSet<Future<File>>();
154 
155       // get every files provided by every FileProvider
156       for (final FutureFileProvider fileProvider : fileProviderItfs.values()) {
157         futureFiles.add(fileProvider.getFile(executorService));
158       }
159 
160       // get every files provided by every FileCollectionProvider
161       for (final FutureFileCollectionProvider collectionProvider : fileCollectionProviderItfs
162           .values()) {
163         futureFiles.addAll(collectionProvider.getFiles(executorService));
164       }
165 
166       archiveFile = executorService.submit(new Callable<File>() {
167 
168         public File call() throws Exception {
169           final Collection<File> inputFiles = new ArrayList<File>(futureFiles
170               .size());
171           File outputFile = getOutputFile();
172           final long outputTimestamp = outputFile.lastModified();
173           boolean relink = false;
174           if (outputTimestamp == 0) relink = true;
175 
176           for (final Future<File> futureFile : futureFiles) {
177             File inputFile;
178             try {
179               inputFile = futureFile.get();
180             } catch (final ExecutionException e) {
181               if (e.getCause() instanceof ADLException)
182                 throw (ADLException) e.getCause();
183               else
184                 throw e;
185             }
186             inputFiles.add(inputFile);
187             if (!relink && inputFile.lastModified() > outputTimestamp) {
188               if (depLogger.isLoggable(Level.FINE))
189                 depLogger.fine("Input file '" + inputFile
190                     + " is more recent than file '" + outputFile
191                     + "', recompile.");
192               relink = true;
193             }
194           }
195           if (!relink && depLogger.isLoggable(Level.FINE))
196             depLogger.fine("Output file '" + outputFile
197                 + "' is up to date, do not recompile.");
198 
199           if (relink) {
200             SourceFileWriter.createOutputDir(outputFile.getParentFile());
201             outputFile = archivate(outputFile, inputFiles);
202           }
203           return outputFile;
204         }
205       });
206     }
207     return archiveFile;
208   }
209 }