001 /* 002 * Copyright (c) 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.metro.tools; 020 021 import java.io.File; 022 import java.io.FileOutputStream; 023 import java.io.OutputStream; 024 import java.io.IOException; 025 import java.util.LinkedList; 026 import java.util.List; 027 import java.beans.IntrospectionException; 028 029 import net.dpml.library.info.Scope; 030 031 import net.dpml.metro.info.Type; 032 import net.dpml.metro.builder.ComponentTypeEncoder; 033 034 import net.dpml.tools.tasks.GenericTask; 035 036 import org.apache.tools.ant.AntClassLoader; 037 import org.apache.tools.ant.BuildException; 038 import org.apache.tools.ant.DynamicElementNS; 039 import org.apache.tools.ant.Project; 040 import org.apache.tools.ant.types.Path; 041 042 /** 043 * Task that handles the construction of catalog of type entries. 044 * 045 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 046 * @version 1.1.3 047 */ 048 public class TypesTask extends GenericTask implements DynamicElementNS 049 { 050 private static final ComponentTypeEncoder COMPONENT_TYPE_ENCODER = 051 new ComponentTypeEncoder(); 052 053 054 private List m_builders = new LinkedList(); 055 056 /** 057 * Create and return a new type builder. 058 * @return the type builder 059 */ 060 public TypeBuilder createType() 061 { 062 Project proj = getProject(); 063 TypeBuilderTask builder = new TypeBuilderTask(); 064 builder.setProject( proj ); 065 m_builders.add( builder ); 066 return builder; 067 } 068 069 /** 070 * Operation used to construct a custom part type directive. 071 * @param uri the part handler uri 072 * @param name the element name 073 * @param qualified the qualified name 074 * @return a dynamic type builder 075 */ 076 public Object createDynamicElement( String uri, String name, String qualified ) 077 { 078 String path = getProject().replaceProperties( uri ); 079 TypeBuilder builder = loadTypeBuilder( path, name ); 080 if( null != builder ) 081 { 082 m_builders.add( builder ); 083 } 084 return builder; 085 } 086 087 /** 088 * Task executaion. 089 */ 090 public void execute() 091 { 092 Project proj = getProject(); 093 Path path = getContext().getPath( Scope.RUNTIME ); 094 File classes = getContext().getTargetClassesMainDirectory(); 095 path.createPathElement().setLocation( classes ); 096 ClassLoader classloader = new AntClassLoader( proj, path ); 097 buildTypes( classloader ); 098 } 099 100 private void buildTypes( ClassLoader classloader ) 101 { 102 final TypeBuilder[] builders = (TypeBuilder[]) m_builders.toArray( new TypeBuilder[0] ); 103 ClassLoader current = Thread.currentThread().getContextClassLoader(); 104 for( int i=0; i<builders.length; i++ ) 105 { 106 final TypeBuilder builder = builders[i]; 107 try 108 { 109 final Type type = builder.buildType( classloader ); 110 OutputStream output = getOutputStream( type ); 111 try 112 { 113 COMPONENT_TYPE_ENCODER.export( type, output ); 114 } 115 finally 116 { 117 try 118 { 119 output.close(); 120 } 121 catch( IOException ioe ) 122 { 123 ioe.printStackTrace(); 124 } 125 } 126 } 127 catch( IntrospectionException e ) 128 { 129 final String error = e.getMessage(); 130 throw new BuildException( error, e, getLocation() ); 131 } 132 catch( BuildException e ) 133 { 134 throw e; 135 } 136 catch( Throwable e ) 137 { 138 final String error = 139 "Internal error while attempting to build types." 140 + "\nCause: " + e.getClass().getName() 141 + "\nMessage: " + e.getMessage(); 142 throw new BuildException( error, e, getLocation() ); 143 } 144 } 145 } 146 147 private OutputStream getOutputStream( Type type ) throws IOException 148 { 149 final String classname = type.getInfo().getClassname(); 150 final String resource = getEmbeddedResourcePath( classname ); 151 final File file = getEmbeddedOutputFile( resource ); 152 file.getParentFile().mkdirs(); 153 return new FileOutputStream( file ); 154 } 155 156 private String getEmbeddedResourcePath( String classname ) 157 { 158 String path = classname.replace( '.', '/' ); 159 String filename = path + ".type"; 160 return filename; 161 } 162 163 private File getEmbeddedOutputFile( String filename ) 164 { 165 File classes = getContext().getTargetClassesMainDirectory(); 166 File destination = new File( classes, filename ); 167 return destination; 168 } 169 170 private TypeBuilder loadTypeBuilder( String uri, String name ) throws BuildException 171 { 172 String urn = uri + ":" + name; 173 Object builder = null; 174 TypeBuilder typeBuilder = null; 175 ClassLoader context = Thread.currentThread().getContextClassLoader(); 176 try 177 { 178 Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); 179 Project proj = getProject(); 180 builder = proj.createDataType( urn ); 181 typeBuilder = (TypeBuilder) builder; 182 return typeBuilder; 183 } 184 catch( ClassCastException e ) 185 { 186 final String error = 187 "The custom type builder [" 188 + builder.getClass().getName() 189 + "] established by the uri [" 190 + urn 191 + "] declared by the element <" 192 + name 193 + "' does not implement the net.dpml.metro.builder.TypeBuilder interface."; 194 throw new BuildException( error, e, getLocation() ); 195 } 196 catch( BuildException e ) 197 { 198 final String error = 199 "Unable to load the plugin from the uri [" 200 + urn 201 + "] to handle the custom type declared by the element <" 202 + name 203 + ">."; 204 throw new BuildException( error, e, getLocation() ); 205 } 206 catch( Throwable e ) 207 { 208 final String error = 209 "Unexpected exception while attempting to load the custom type handler with the uri [" 210 + urn 211 + "] declared by the element <" 212 + name 213 + ">."; 214 throw new BuildException( error, e, getLocation() ); 215 } 216 finally 217 { 218 Thread.currentThread().setContextClassLoader( context ); 219 } 220 } 221 }