001 /* 002 * Copyright 2005 Stephen J. McConnell 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.tools.tasks; 020 021 import java.io.File; 022 import java.io.OutputStream; 023 import java.io.FileOutputStream; 024 import java.io.IOException; 025 import java.net.URI; 026 import java.util.ArrayList; 027 import java.util.List; 028 029 import net.dpml.lang.Classpath; 030 import net.dpml.lang.Category; 031 import net.dpml.lang.Info; 032 import net.dpml.lang.Part; 033 import net.dpml.lang.PartDecoder; 034 035 import net.dpml.library.Type; 036 import net.dpml.library.Resource; 037 038 import net.dpml.transit.Artifact; 039 040 import net.dpml.tools.Context; 041 import net.dpml.tools.BuildError; 042 043 import net.dpml.util.DefaultLogger; 044 045 import org.apache.tools.ant.Project; 046 import org.apache.tools.ant.BuildException; 047 048 import org.w3c.dom.Element; 049 050 /** 051 * Creation of an part definition in XML. 052 * 053 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 054 * @version 1.1.3 055 */ 056 public class PartTask extends GenericTask 057 { 058 /** 059 * Constant artifact type for a plugin. 060 */ 061 public static final String TYPE = "part"; 062 063 /** 064 * Property key used to identify the plugin title. 065 */ 066 public static final String PLUGIN_TITLE_KEY = "project.title"; 067 068 /** 069 * Property key used to identify the plugin description. 070 */ 071 public static final String PLUGIN_DESCRIPTION_KEY = "project.description"; 072 073 /** 074 * Property key used to identify a custom plugin handler classname. 075 */ 076 public static final String PLUGIN_HANDLER_KEY = "project.plugin.handler"; 077 078 private boolean m_test = false; 079 080 private File m_output; 081 082 /** 083 * Set the test build policy. The default is to include 084 * the project artifact in the classpath of a created part, however - in a 085 * test scenario we don't want to do this. Setting test to true will result 086 * in the association of a local file uri to the project resource. 087 * 088 * @param test true if this is a local test part 089 */ 090 public void setTest( boolean test ) 091 { 092 m_test = test; 093 } 094 095 /** 096 * Override the default output destination. 097 * 098 * @param file the overriding destination 099 */ 100 public void setDest( File file ) 101 { 102 m_output = file; 103 } 104 105 /** 106 * Task execution. 107 */ 108 public void execute() 109 { 110 Resource resource = getResource(); 111 Part part = build( resource ); 112 writePart( part ); 113 } 114 115 /** 116 * Externalize the part as a deliverable. 117 * @param part the part to be externalized as XML 118 */ 119 public void writePart( Part part ) 120 { 121 File file = getOutputFile(); 122 if( file.exists() ) 123 { 124 try 125 { 126 Part existing = Part.load( file.toURI(), false ); 127 if( part.equals( existing ) ) 128 { 129 return; 130 } 131 } 132 catch( Throwable e ) 133 { 134 final String error = 135 "Part comparisom failure."; 136 throw new BuildError( error, e ); 137 } 138 } 139 140 log( "Building part: " + file ); 141 try 142 { 143 file.createNewFile(); 144 final OutputStream output = new FileOutputStream( file ); 145 try 146 { 147 part.encode( output ); 148 checksum( file ); 149 asc( file ); 150 } 151 finally 152 { 153 try 154 { 155 output.close(); 156 } 157 catch( IOException e ) 158 { 159 e.printStackTrace(); 160 } 161 } 162 } 163 catch( Exception e ) 164 { 165 final String error = 166 "Part externalization error."; 167 throw new BuildError( error, e ); 168 } 169 } 170 171 private File getOutputFile() 172 { 173 if( null != m_output ) 174 { 175 return m_output; 176 } 177 else 178 { 179 Context context = getContext(); 180 final String path = context.getLayoutFilename( TYPE ); 181 final File deliverables = context.getTargetDeliverablesDirectory(); 182 final File parts = new File( deliverables, TYPE + "s" ); 183 final File file = new File( parts, path ); 184 parts.mkdirs(); 185 return file; 186 } 187 } 188 189 /** 190 * Build the part definition. 191 * @param resource the resource 192 * @return the part 193 */ 194 protected Part build( Resource resource ) 195 { 196 try 197 { 198 Info info = getInfo( resource ); 199 Classpath classpath = getClasspath( resource ); 200 Type type = resource.getType( TYPE ); 201 Element element = type.getElement(); 202 PartDecoder decoder = PartDecoder.getInstance(); 203 DefaultLogger logger = new DefaultLogger( getTaskName() ); 204 return decoder.build( logger, info, classpath, element, resource ); 205 } 206 catch( Throwable e ) 207 { 208 final String error = 209 "Internal error while attempting to build an external part definition." 210 + "\nResource: " + resource; 211 throw new BuildError( error, e, getLocation() ); 212 } 213 } 214 215 /** 216 * Construct the info object based on properties declared by the 217 * supplied resource. 218 * @param resource the resource 219 * @return the info descriptor 220 */ 221 protected Info getInfo( Resource resource ) 222 { 223 Artifact artifact = resource.getArtifact( TYPE ); 224 URI uri = artifact.toURI(); 225 String title = resource.getInfo().getTitle(); 226 String description = resource.getInfo().getDescription(); 227 return new Info( uri, title, description ); 228 } 229 230 private String getTitle( Resource resource ) 231 { 232 return resource.getProperty( PLUGIN_TITLE_KEY ); 233 } 234 235 private String getDescription( Resource resource ) 236 { 237 return resource.getProperty( PLUGIN_DESCRIPTION_KEY ); 238 } 239 240 /** 241 * Construct the classpath for the supplied resource. 242 * @param resource the resource 243 * @return the classpath 244 * @exception IOException is an IO error occurs 245 */ 246 protected Classpath getClasspath( Resource resource ) throws IOException 247 { 248 URI[] sysUris = getURIs( resource, Category.SYSTEM ); 249 URI[] publicUris = getURIs( resource, Category.PUBLIC ); 250 URI[] protectedUris = getURIs( resource, Category.PROTECTED ); 251 URI[] privateUris = getURIs( resource, Category.PRIVATE, true ); 252 return new Classpath( sysUris, publicUris, protectedUris, privateUris ); 253 } 254 255 private URI[] getURIs( Resource resource, Category category ) throws IOException 256 { 257 return getURIs( resource, category, false ); 258 } 259 260 private URI[] getURIs( Resource resource, Category category, boolean self ) throws IOException 261 { 262 Resource[] resources = resource.getClasspathProviders( category ); 263 ArrayList list = new ArrayList(); 264 for( int i=0; i<resources.length; i++ ) 265 { 266 Resource r = resources[i]; 267 addURI( list, r ); 268 } 269 if( self ) 270 { 271 addURI( list, resource ); 272 } 273 URI[] uris = (URI[]) list.toArray( new URI[0] ); 274 return uris; 275 } 276 277 private void addURI( List list, Resource resource ) throws IOException 278 { 279 if( resource.isa( "jar" ) ) 280 { 281 try 282 { 283 URI uri = toURI( resource ); 284 list.add( uri ); 285 } 286 catch( Exception e ) 287 { 288 final String error = 289 "Unexpected error while attempting to resolve resource."; 290 IOException ioe = new IOException( error ); 291 ioe.initCause( e ); 292 throw ioe; 293 } 294 } 295 } 296 297 private URI toURI( Resource resource ) throws Exception 298 { 299 if( m_test && resource.equals( getResource() ) ) 300 { 301 File local = getContext().getTargetDeliverable( "jar" ); 302 return local.toURI(); 303 } 304 else 305 { 306 return resource.getArtifact( "jar" ).toURI(); 307 } 308 } 309 310 /** 311 * Get the project definition. 312 * @param project the project 313 * @return the build context 314 */ 315 protected Context getContext( Project project ) 316 { 317 Context context = (Context) project.getReference( "project.context" ); 318 context.init(); 319 return context; 320 } 321 }