View Javadoc

1   /**
2    * Copyright 2005-2006 the original author or authors.
3    *
4    * Licensed under the Gnu General Pubic License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    *
8    *      http://www.opensource.org/licenses/gpl-license.php
9    *
10   * This program 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.
13   * See the Gnu General Public License for more details.
14   */
15  package org.figure8.join.services.scripting.ant.tasks;
16  
17  import org.figure8.join.services.remoting.ArtifactService;
18  import org.figure8.join.services.remoting.InvalidSessionException;
19  import org.figure8.join.services.remoting.beans.RemoteComponent;
20  import org.figure8.join.services.security.InvalidLoginException;
21  
22  import org.apache.tools.ant.Project;
23  import org.apache.tools.ant.BuildException;
24  import org.apache.tools.ant.DirectoryScanner;
25  import org.apache.tools.ant.types.FileSet;
26  
27  import java.io.File;
28  import java.util.ArrayList;
29  import java.rmi.RemoteException;
30  /**
31   * This an Ant task to use for binding a component being built/discovered to a
32   * specified assembly and/or build. <br/>
33   * As an example, use a <i>bindcomponent</i> that way to ensure attaching a
34   * component to an assembly : <br/>
35   * <code>
36   * &gt;bindcomponent type="myComponent" assembly="myassembly-r1.0-v01"
37   *       version="001" url="http://mydomain.org:8080/join"&lt;<br/>
38   *    &gt;fileset dir="${src.dir}/myComponent" includes="*.*"/&lt;
39   * &gt;/bindcomponent&lt;
40   * </code><br/>
41   * That task will then connect to Join system denoted by url to see if component of
42   * type "myComponent" exists with version "001". If no, il will create it into Join
43   * system, specifying its size using files from nested filesets. Finally, it will
44   * attach existing or newly created component to the existing "myassembly-r1.0-v01"
45   * assembly.
46   * <br/>
47   * This task is very useful when creating an assembly using an Ant script and
48   * introspecting its content, searching for contained components : it allows you
49   * to trace what you have dynamically discovered.
50   * <br/>
51   * When component version is not easily extractable, you can replace <i>version</i>
52   * attribute by <i>extractor</i> and thus specifying a {@see VersionInfoExtractor}
53   * implementation to use for extracting version info from files in nested filesets.
54   * <br/>
55   * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
56   * @version $Revision: 1.1 $
57   */
58  public class BindComponentTask extends RemoteServiceTask{
59  
60     // Attributes ---------------------------------------------------------------
61  
62     /** The component type to bind a component for */
63     private String type;
64     /** The assembly that contains component */
65     private String assembly;
66     /** The component version information */
67     private String version;
68     /** The component version extractor implementation to use */
69     private Class extractor;
70     /** A list of Ant filesets to process */
71     private ArrayList fileSets = new ArrayList();
72  
73     /** The version information extractor instance used for retrieving component version */
74     protected VersionInfoExtractor versionExtractor = null;
75  
76  
77     // Constructors -------------------------------------------------------------
78  
79     /** Creates a new instance of BindComponentTask */
80     public BindComponentTask(){
81     }
82  
83  
84     // Public -------------------------------------------------------------------
85  
86     /** @return The component type to bind a component for */
87     public String getType(){
88        return type;
89     }
90     /** @param type The component type to bind a component for */
91     public void setType(String type){
92        this.type = type;
93     }
94     /** @return The assembly that contains component */
95     public String getAssembly(){
96        return assembly;
97     }
98     /** @param assembly The assembly that contains component */
99     public void setAssembly(String assembly){
100       this.assembly = assembly;
101    }
102    /** @return The component version information */
103    public String getVersion(){
104       return version;
105    }
106    /** @param version The component version information */
107    public void setVersion(String version){
108       this.version = version;
109    }
110    /** @return The component version extractor implementation to use */
111    public Class getExtractor(){
112       return extractor;
113    }
114    /** @param extractor The component version extractor implementation to use */
115    public void setExtractor(Class extractor){
116       this.extractor = extractor;
117    }
118 
119    /**
120     * Adds a set of files (nested fileset element)
121     * @param set A FileSet containing files to process
122     */
123    public void addFileset(FileSet set){
124       fileSets.add(set);
125    }
126 
127 
128    // Override of Task ---------------------------------------------------------
129 
130    /**
131     * Execute this task : do the binding of configured component
132     * @throws BuildException if something wrong occurs during task process
133     */
134    public void execute() throws BuildException{
135       // Validate task attributes.
136       validateAttributes();
137       // Get files from file sets.
138       File[] files = getFilesFromFileSets();
139 
140       // Try to retrieve this component version.
141       if (version == null || version.length() == 0){
142          try {version = versionExtractor.extractVersionInfo(files);}
143          catch (Exception e){
144             log("Exception while extracting version: " + e.getMessage(), Project.MSG_ERR);
145             throw new BuildException("Problem during version extraction: " + e.getMessage(), getLocation());
146          }
147       }
148       // Bind the component represented by files.
149       bindComponent(version, files);
150    }
151 
152 
153    // Protected ----------------------------------------------------------------
154 
155    /**
156     * Validate this task attributes before execution.
157     * @throws BuildException if a mandatory attribute is not present
158     */
159    protected void validateAttributes() throws BuildException{
160       // Check that type is present.
161       if (type == null || type.length() == 0)
162          throw new BuildException("No component type specified for bindComponent", getLocation());
163       // Check that assembly is present.
164       if (assembly == null || assembly.length() == 0)
165          throw new BuildException("No assembly specified for bindComponent", getLocation());
166       // Check that version or extractor is present.
167       if (version == null && extractor == null)
168          throw new BuildException("No version information nor extractor implementation specified for bindComponent",
169                  getLocation());
170       // Check that there's at least one fileset.
171       if (fileSets.isEmpty())
172          throw new BuildException("No FileSet specified for bindComponent", getLocation());
173 
174       // Try to build an extractor instance and cast it.
175       if (extractor != null){
176          try {versionExtractor = (VersionInfoExtractor)extractor.newInstance();}
177          catch (Exception e){
178             log("Exception while creating VersionInfoExtractor impl: " + e.getMessage(), Project.MSG_ERR);
179             throw new BuildException("Extractor must be an implementation of VersionInfoExtractor", getLocation());
180          }
181       }
182    }
183 
184    /**
185     * Build an array of included files.
186     * @return An array of files build from fileSets
187     */
188    protected File[] getFilesFromFileSets(){
189       // Deal with the filesets ...
190       ArrayList fileList = new ArrayList();
191       for (int i=0; i < fileSets.size(); i++){
192          // Get files from each filesets.
193          FileSet fs = (FileSet)fileSets.get(i);
194          DirectoryScanner ds = fs.getDirectoryScanner(getProject());
195          File fromdir = fs.getDir(getProject());
196          String[] srcfiles = ds.getIncludedFiles();
197          for (int j=0; j < srcfiles.length; j++)
198             fileList.add(new File(fromdir, srcfiles[j]));
199       }
200 
201       // Convert the files list into an array.
202       File[] files = new File[fileList.size()];
203       files = (File[])fileList.toArray(files);
204       return files;
205    }
206 
207    /**
208     * Do the real component binding (remote service invocation)
209     * @param files The files representing component
210     * @throws BuildException if service invocation fails
211     */
212    protected void bindComponent(String version, File[] files) throws BuildException{
213       // Retrieve remote service, security token and parameters map.
214       ArtifactService service = (ArtifactService)retrieveRemoteService("ArtifactService", ArtifactService.class);
215 
216       String token = null;
217       try{
218          // Login and invoke service.
219          token = loginToRemoteService(service);
220          long size = computeSizeInBytes(files);
221          log("Bind the '" + type + "' component " + version + " using remote service", Project.MSG_DEBUG);
222          RemoteComponent component = new RemoteComponent(version, size, type);
223          service.bindComponent(token, component, assembly);
224       }
225       catch (InvalidLoginException ile){
226          // Log diagnostic messages and wrap into a BuildException.
227          log("InvalidLoginException while logging to remote service: " + ile.getMessage(), Project.MSG_ERR);
228          throw new BuildException("Exception while logging to remote service", ile, getLocation());
229       }
230       catch (InvalidSessionException ise){
231          // Log diagnostic messages and wrap into a BuildException.
232          log("InvalidSessionException while invoking remote service: " + ise.getMessage(), Project.MSG_ERR);
233          throw new BuildException("Exception while invoking remote service", ise, getLocation());
234       }
235       catch (RemoteException re){
236          // Log diagnostic messages and wrap into a BuildException.
237          log("RemoteException while logging to remote service: " + re.getMessage(), Project.MSG_ERR);
238          throw new BuildException("Exception while logging to remote service", re, getLocation());
239       }
240       catch (Exception e){
241          // Log diagnostic messages and wrap into a BuildException.
242          log("Unexpected exception while binding component: " + e.getMessage(), Project.MSG_ERR);
243          throw new BuildException("Unexpected exception while binding component", e, getLocation());
244       }
245       // Release service and return.
246       releaseRemoteService(service, token);
247    }
248 
249    /**
250     * Compute the sum of files weigth. This sum is expressed in bytes.
251     * @param files The set of files to sum weigth.
252     */
253    protected long computeSizeInBytes(File[] files) throws Exception{
254       long sum = 0;
255       for (int i=0; i<files.length ; i++)
256          sum += files[i].length();
257       return sum;
258    }
259 }
260