-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathJavaCompiler.cfc
More file actions
178 lines (145 loc) · 6.92 KB
/
JavaCompiler.cfc
File metadata and controls
178 lines (145 loc) · 6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<cfcomponent hint="Compiles Java source dirs to an array of .jar files." output="false">
<!------------------------------------------- PUBLIC ------------------------------------------->
<cffunction name="init" hint="Constructor" access="public" returntype="JavaCompiler" output="false">
<cfargument name="jarDirectory" hint="the directory to build the .jar file in, defaults to ./tmp" type="string" required="No" default="#getDirectoryFromPath(getMetadata(this).path)#/tmp">
<cfscript>
var data = {};
var defaultCompiler = "com.sun.tools.javac.api.JavacTool";
//we have to manually go looking for the compiler
try
{
data.compiler = getPageContext().getClass().getClassLoader().loadClass(defaultCompiler).newInstance();
}
catch(any exc)
{
println("Error loading compiler:");
println(exc.toString());
}
/*
If not by THIS point do we have a compiler, then throw an exception
*/
if(NOT StructKeyExists(data, "compiler"))
{
throwException("javaCompiler.NoCompilerAvailableException",
"No Java Compiler is available",
"There is no Java Compiler available. Make sure tools.jar is in your classpath and you are running Java 1.6+");
}
setCompiler(data.compiler);
setJarDirectory(arguments.jarDirectory);
return this;
</cfscript>
</cffunction>
<cffunction name="compile" hint="compiles Java to bytecode, and returns a JAR" access="public" returntype="any" output="false">
<cfargument name="directoryArray" hint="array of directories to compile" type="array" required="Yes">
<cfargument name="classLoader" hint="a optional URLClassloader to use as the parent for compilation" type="any" required="false">
<cfargument name="jarName" hint="The name of the jar file. Defaults to a UUID" type="string" required="false" default="#createUUID()#.jar">
<cfscript>
//setup file manager with default exception handler, default locale, and default character set
var fileManager = getCompiler().getStandardFileManager(JavaCast("null", ""), JavaCast("null", ""), JavaCast("null", ""));
var qFiles = 0;
var fileArray = [];
var directoryToCompile = 0;
var fileObjects = 0;
var osw = createObject("java", "java.io.StringWriter").init();
var options = [];
var compilePass = 0;
var jarPath = getJarDirectory() & "/" & arguments.jarName;
</cfscript>
<cfloop array="#arguments.directoryArray#" index="directoryToCompile">
<cfdirectory action="list" directory="#directoryToCompile#" name="qFiles" recurse="true" filter="*.java">
<cfloop query="qFiles">
<cfscript>
ArrayAppend(fileArray, qFiles.directory & "/" & qFiles.name);
</cfscript>
</cfloop>
<cfscript>
if(structKeyExists(arguments, "classLoader"))
{
options = addClassLoaderFiles(options, arguments.classLoader, arguments.directoryArray);
}
fileObjects = fileManager.getJavaFileObjectsFromStrings(fileArray);
</cfscript>
</cfloop>
<cfscript>
//does the compilation
compilePass = getCompiler().getTask(osw, fileManager, JavaCast("null", ""), options, JavaCast("null", ""), fileObjects).call();
if(NOT compilePass)
{
throwException("javacompiler.SourceCompilationException", "There was an error compiling your source code", osw.toString());
}
</cfscript>
<!--- wrap it up in a jar --->
<cfloop array="#arguments.directoryArray#" index="directoryToCompile">
<!--- do this again, as if there ARE files in it, we should create a .jar --->
<cfdirectory action="list" directory="#directoryToCompile#" name="qFiles">
<!--- can't do zips on empty directories --->
<cfif qFiles.recordCount>
<cfzip action="zip" file="#jarPath#" recurse="yes" source="#directoryToCompile#" overwrite="no">
</cfif>
</cfloop>
<!--- we won't bother with an manifest, as we don't really need one --->
<cfreturn jarPath />
</cffunction>
<cffunction name="getVersion" hint="returns the version number" access="public" returntype="string" output="false">
<cfreturn "0.1.b" />
</cffunction>
<!------------------------------------------- PACKAGE ------------------------------------------->
<!------------------------------------------- PRIVATE ------------------------------------------->
<cffunction name="addClassLoaderFiles" hint="adds a set of files to the file manager from the urlclassloader" access="private" returntype="array" output="false">
<cfargument name="options" hint="the options array" type="array" required="Yes">
<cfargument name="classLoader" hint="URLClassloader to use as the parent for compilation" type="any" required="true">
<cfargument name="directoryArray" hint="array of directories to compile" type="array" required="Yes">
<cfscript>
var urls = 0;
var uri = 0;
var classPaths = createObject("java", "java.lang.StringBuilder").init();
var File = createObject("java", "java.io.File");
var path = 0;
</cfscript>
<!--- add in the classloader, and all its parents --->
<cfloop condition="#structKeyExists(arguments, "classLoader")#">
<cfset urls = arguments.classLoader.getURLs()>
<cfloop array="#urls#" index="uri">
<cfscript>
classPaths.append(uri.getFile()).append(File.pathSeparator);
</cfscript>
</cfloop>
<cfset arguments.classLoader = arguments.classLoader.getParent()>
</cfloop>
<!--- add in the folders we are compiling from --->
<cfloop array="#arguments.directoryArray#" index="path">
<cfset classPaths.append(path).append(File.pathSeparator)>
</cfloop>
<cfscript>
ArrayAppend(arguments.options, "-classpath");
ArrayAppend(arguments.options, classPaths.toString());
return arguments.options;
</cfscript>
</cffunction>
<cffunction name="getCompiler" access="private" returntype="any" output="false">
<cfreturn instance.Compiler />
</cffunction>
<cffunction name="setCompiler" access="private" returntype="void" output="false">
<cfargument name="Compiler" type="any" required="true">
<cfset instance.Compiler = arguments.Compiler />
</cffunction>
<cffunction name="getJarDirectory" access="private" returntype="string" output="false">
<cfreturn instance.jarDirectory />
</cffunction>
<cffunction name="setJarDirectory" access="private" returntype="void" output="false">
<cfargument name="jarDirectory" type="string" required="true">
<cfset instance.jarDirectory = arguments.jarDirectory />
</cffunction>
<cffunction name="throwException" access="private" hint="Throws an Exception" output="false">
<cfargument name="type" hint="The type of exception" type="string" required="Yes">
<cfargument name="message" hint="The message to accompany the exception" type="string" required="Yes">
<cfargument name="detail" type="string" hint="The detail message for the exception" required="No" default="">
<cfthrow type="#arguments.type#" message="#arguments.message#" detail="#arguments.detail#">
</cffunction>
<cffunction name="println" hint="" access="private" returntype="void" output="false">
<cfargument name="str" hint="" type="string" required="Yes">
<cfscript>
createObject("Java", "java.lang.System").out.println(arguments.str);
</cfscript>
</cffunction>
</cfcomponent>