PageRenderTime 476ms CodeModel.GetById 202ms app.highlight 18ms RepoModel.GetById 193ms app.codeStats 2ms

/wheels/vendor/javaloader/JavaLoader.cfc

http://cfwheels.googlecode.com/
ColdFusion CFScript | 500 lines | 381 code | 96 blank | 23 comment | 13 complexity | 1dc1f533428c713f5eb12c1b8a53dc90 MD5 | raw file
  1<!--- Document Information -----------------------------------------------------
  2
  3Title:      JavaLoader.cfc
  4
  5Author:     Mark Mandel
  6Email:      mark@compoundtheory.com
  7
  8Website:    http://www.compoundtheory.com
  9
 10Purpose:    Utlitity class for loading Java Classes
 11
 12------------------------------------------------------------------------------->
 13<cfcomponent name="JavaLoader" hint="Loads External Java Classes, while providing access to ColdFusion classes">
 14
 15<cfscript>
 16	instance = StructNew();
 17	instance.static.uuid = "A0608BEC-0AEB-B46A-0E1E1EC5F3CE7C9C";
 18</cfscript>
 19
 20<cfimport taglib="tags" prefix="jl">
 21
 22<!------------------------------------------- PUBLIC ------------------------------------------->
 23
 24<cffunction name="init" hint="Constructor" access="public" returntype="JavaLoader" output="false">
 25	<cfargument name="loadPaths" hint="An array of directories of classes, or paths to .jar files to load" type="array" default="#ArrayNew(1)#" required="no">
 26	<cfargument name="loadColdFusionClassPath" hint="Loads the ColdFusion libraries" type="boolean" required="No" default="false">
 27	<cfargument name="parentClassLoader" hint="(Expert use only) The parent java.lang.ClassLoader to set when creating the URLClassLoader" type="any" default="" required="false">
 28	<cfargument name="sourceDirectories" hint="Directories that contain Java source code that are to be dynamically compiled" type="array" required="No">
 29	<cfargument name="compileDirectory" hint="the directory to build the .jar file for dynamic compilation in, defaults to ./tmp" type="string" required="No" default="#getDirectoryFromPath(getMetadata(this).path)#/tmp">
 30	<cfargument name="trustedSource" hint="Whether or not the source is trusted, i.e. it is going to change? Defaults to false, so changes will be recompiled and loaded" type="boolean" required="No" default="false">
 31
 32	<cfscript>
 33		initUseJavaProxyCFC();
 34
 35		if(arguments.loadColdFusionClassPath)
 36		{
 37			//arguments.parentClassLoader = createObject("java", "java.lang.Thread").currentThread().getContextClassLoader();
 38			//can't use above, as doesn't work in some... things
 39
 40			arguments.parentClassLoader = getPageContext().getClass().getClassLoader();
 41
 42			//arguments.parentClassLoader = createObject("java", "java.lang.ClassLoader").getSystemClassLoader();
 43			//can't use the above, it doesn't have the CF stuff in it.
 44		}
 45
 46		setClassLoadPaths(arguments.loadPaths);
 47		setParentClassLoader(arguments.parentClassLoader);
 48
 49		ensureNetworkClassLoaderOnServerScope();
 50
 51		loadClasses();
 52
 53		if(structKeyExists(arguments, "sourceDirectories") AND ArrayLen(arguments.sourceDirectories))
 54		{
 55			setJavaCompiler(createObject("component", "JavaCompiler").init(arguments.compileDirectory));
 56			setSourceDirectories(arguments.sourceDirectories);
 57			setCompileDirectory(arguments.compileDirectory);
 58
 59            setTrustedSource(arguments.trustedSource);
 60
 61			compileSource();
 62
 63			setSourceLastModified(calculateSourceLastModified());
 64
 65			//do the method switching for non-trusted source
 66			if(NOT arguments.trustedSource)
 67			{
 68				variables.createWithoutCheck = variables.create;
 69
 70				StructDelete(this, "create");
 71				StructDelete(variables, "create");
 72
 73				this.create = variables.createWithSourceCheck;
 74			}
 75		}
 76
 77		return this;
 78	</cfscript>
 79</cffunction>
 80
 81<cffunction name="create" hint="Retrieves a reference to the java class. To create a instance, you must run init() on this object" access="public" returntype="any" output="false">
 82	<cfargument name="className" hint="The name of the class to create" type="string" required="Yes">
 83	<cfscript>
 84		try
 85		{
 86			//do this in one line just for speed.
 87			return createJavaProxy(getURLClassLoader().loadClass(arguments.className));
 88		}
 89		catch(java.lang.ClassNotFoundException exc)
 90		{
 91			throwException("javaloader.ClassNotFoundException", "The requested class could not be found.", "The requested class '#arguments.className#' could not be found in the loaded jars/directories.");
 92		}
 93	</cfscript>
 94</cffunction>
 95
 96<cffunction name="getURLClassLoader" hint="Returns the com.compoundtheory.classloader.NetworkClassLoader in case you need access to it" access="public" returntype="any" output="false">
 97	<cfreturn instance.ClassLoader />
 98</cffunction>
 99
100<cffunction name="getVersion" hint="Retrieves the version of the loader you are using" access="public" returntype="string" output="false">
101	<cfreturn "1.0">
102</cffunction>
103
104<!------------------------------------------- PACKAGE ------------------------------------------->
105
106<!------------------------------------------- PRIVATE ------------------------------------------->
107
108<cffunction name="createWithSourceCheck" hint="does the create call, but first makes a source check" access="private" returntype="any" output="false">
109	<cfargument name="className" hint="The name of the class to create" type="string" required="Yes">
110	<cfscript>
111		var dateLastModified = calculateSourceLastModified();
112
113		/*
114			If the source has changed in any way, recompile and load
115		*/
116		if(dateCompare(dateLastModified, getSourceLastModified()) eq 1)
117		{
118			loadClasses();
119			compileSource();
120		}
121
122		//if all the comilation goes according to plan, set the date last modified
123		setSourceLastModified(dateLastModified);
124
125		return createWithoutCheck(argumentCollection=arguments);
126    </cfscript>
127</cffunction>
128
129<cffunction name="loadClasses" hint="loads up the classes in the system" access="private" returntype="void" output="false">
130	<cfscript>
131		var iterator = getClassLoadPaths().iterator();
132		var file = 0;
133		var classLoader = 0;
134		var networkClassLoaderClass = 0;
135		var networkClassLoaderProxy = 0;
136
137		networkClassLoaderClass = getServerURLClassLoader().loadClass("com.compoundtheory.classloader.NetworkClassLoader");
138
139		networkClassLoaderProxy = createJavaProxy(networkClassLoaderClass);
140
141		if(isObject(getParentClassLoader()))
142		{
143			classLoader = networkClassLoaderProxy.init(getParentClassLoader());
144		}
145		else
146		{
147			classLoader = networkClassLoaderProxy.init();
148		}
149
150		while(iterator.hasNext())
151		{
152			file = createObject("java", "java.io.File").init(iterator.next());
153			if(NOT file.exists())
154			{
155				throwException("javaloader.PathNotFoundException", "The path you have specified could not be found", file.getAbsolutePath() & " does not exist");
156			}
157
158			classLoader.addUrl(file.toURL());
159		}
160
161		setURLClassLoader(classLoader);
162    </cfscript>
163</cffunction>
164
165<cffunction name="compileSource" hint="compile dynamic source" access="private" returntype="void" output="false">
166	<cfscript>
167		var dir = 0;
168		var path = 0;
169
170		var paths = 0;
171		var file = 0;
172		var counter = 1;
173		var len = 0;
174		var directories = 0;
175
176		//do check to see if the compiled jar is already there
177		var jarName = calculateJarName(getSourceDirectories());
178		var jar = getCompileDirectory() & "/" & jarName;
179    </cfscript>
180
181    <cfif fileExists(jar)>
182        <cfif isTrustedSource()>
183            <!--- add that jar to the classloader --->
184            <cfset file = createObject("java", "java.io.File").init(jar)>
185            <cfset getURLClassLoader().addURL(file.toURL())>
186            <cfreturn />
187        <cfelse>
188            <cffile action="delete" file="#jar#"/>
189        </cfif>
190    </cfif>
191
192	<cftry>
193	    <cfset path = getCompileDirectory() & "/" & createUUID()/>
194
195		<cfdirectory action="create" directory="#path#">
196
197		<cfscript>
198			//first we copy the source to our tmp dir
199			directories = getSourceDirectories();
200			len = arraylen(directories);
201			for(; counter lte len; counter = counter + 1)
202			{
203				dir = directories[counter];
204				directoryCopy(dir, path);
205			}
206
207			//then we compile it, and grab that jar
208
209			paths = ArrayNew(1); //have to write it this way so CF7 compiles
210			ArrayAppend(paths, path);
211
212			jar = getJavaCompiler().compile(paths, getURLClassLoader(), jarName);
213        </cfscript>
214
215		<!--- add that jar to the classloader --->
216		<cfset file = createObject("java", "java.io.File").init(jar)>
217		<cfset getURLClassLoader().addURL(file.toURL())>
218
219		<!--- delete the files --->
220		<cfif directoryExists(path)>
221			<cfdirectory action="delete" recurse="true" directory="#path#">
222		</cfif>
223
224        <!--- save the file for when trusted source is on ---->
225		<cfif fileExists(jar) AND NOT isTrustedSource()>
226			<cffile action="delete" file="#jar#" />
227		</cfif>
228
229		<cfcatch>
230			<!--- make sure the files are deleted --->
231			<cfif directoryExists(path)>
232				<cfdirectory action="delete" recurse="true" directory="#path#">
233			</cfif>
234
235			<cfrethrow>
236		</cfcatch>
237	</cftry>
238</cffunction>
239
240<cffunction name="calculateJarName" hint="returns the jar file name for a directory array" access="private" returntype="string" output="false">
241    <cfargument name="directoryArray" hint="array of directories to compile" type="array" required="Yes">
242    <cfscript>
243        var file = hash(arrayToList(arguments.directoryArray)) & ".jar";
244
245        return file;
246    </cfscript>
247</cffunction>
248
249<cffunction name="calculateSourceLastModified" hint="returns what the source last modified was" access="private" returntype="date" output="false">
250	<cfscript>
251		var lastModified = createDate(1900, 1, 1);
252		var dir = 0;
253		var qLastModified = 0;
254		var directories = getSourceDirectories();
255		var len = arraylen(directories);
256		var counter = 0;
257    </cfscript>
258
259	<!--- cf7 syntax. Yuck. --->
260	<cfloop from="1" to="#len#" index="counter">
261		<cfset dir = directories[counter]>
262		<jl:directory action="list" directory="#dir#" recurse="true"
263					type="file"
264					sort="dateLastModified desc"
265					name="qLastModified">
266		<cfscript>
267			//it's possible there are no source files.
268			if(qLastModified.recordCount)
269			{
270				//get the latest date modified
271				if(dateCompare(lastModified, qlastModified.dateLastModified) eq -1)
272				{
273					/*
274						This is here, because cfdirectory only ever gives you minute accurate modified
275						date, which is not good enough.
276					*/
277					lastModified = createObject("java", "java.util.Date").init(createObject("java", "java.io.File").init(qLastModified.directory & "/" & qLastModified.name).lastModified());
278				}
279			}
280			else
281			{
282				lastModified = Now();
283			}
284        </cfscript>
285	</cfloop>
286
287	<cfreturn lastModified />
288</cffunction>
289
290<cffunction name="ensureNetworkClassLoaderOnServerScope"
291			hint="makes sure there is a URL class loader on the server scope that can load me up some networkClassLoader goodness"
292			access="private" returntype="void" output="false">
293	<cfscript>
294		var Class = createObject("java", "java.lang.Class");
295		var Array = createObject("java", "java.lang.reflect.Array");
296		var jars = queryJars();
297		var iterator = jars.iterator();
298		var file = 0;
299		var urls = Array.newInstance(Class.forName("java.net.URL"), ArrayLen(jars));
300		var counter = 0;
301		var urlClassLoader = 0;
302		var key = instance.static.uuid & "." & getVersion();
303	</cfscript>
304
305	<cfif NOT StructKeyExists(server, key)>
306    	<cflock name="javaloader.networkclassloader" throwontimeout="true" timeout="60">
307    	<cfscript>
308    		if(NOT StructKeyExists(server, key))
309    		{
310				while(iterator.hasNext())
311				{
312					Array.set(urls, counter, createObject("java", "java.io.File").init(iterator.next()).toURL());
313					counter = counter + 1;
314				}
315
316				urlClassLoader = createObject("java", "java.net.URLClassLoader").init(urls);
317
318				//put it on the server scope
319				server[key] = urlClassLoader;
320			}
321    	</cfscript>
322    	</cflock>
323    </cfif>
324</cffunction>
325
326<cffunction name="createJavaProxy" hint="create a javaproxy, dependent on CF server settings" access="private" returntype="any" output="false">
327	<cfargument name="class" hint="the java class to create the proxy with" type="any" required="Yes">
328	<cfscript>
329		return createObject("java", "coldfusion.runtime.java.JavaProxy").init(arguments.class);
330	</cfscript>
331</cffunction>
332
333<cffunction name="createJavaProxyCFC" hint="create a javaproxy, dependent on CF server settings" access="private" returntype="any" output="false">
334	<cfargument name="class" hint="the java class to create the proxy with" type="any" required="Yes">
335	<cfscript>
336		return createObject("component", "JavaProxy")._init(arguments.class);
337	</cfscript>
338</cffunction>
339
340<cffunction name="initUseJavaProxyCFC" hint="initialise whether or not to use the JavaProxy CFC instead of the coldfusion java object" access="private" returntype="string" output="false">
341	<cfscript>
342		try
343		{
344			createObject("java", "coldfusion.runtime.java.JavaProxy");
345		}
346		catch(Object exc)
347		{
348			//do method replacement, as it will be much faster long term
349			variables.createJavaProxy = variables.createJavaProxyCFC;
350		}
351	</cfscript>
352</cffunction>
353
354<cffunction name="queryJars" hint="pulls a query of all the jars in the /resources/lib folder" access="private" returntype="array" output="false">
355	<cfscript>
356		var qJars = 0;
357		//the path to my jar library
358		var path = getDirectoryFromPath(getMetaData(this).path) & "lib/";
359		var jarList = "";
360		var aJars = ArrayNew(1);
361		var libName = 0;
362	</cfscript>
363
364	<cfdirectory action="list" name="qJars" directory="#path#" filter="*.jar" sort="name desc"/>
365	<cfloop query="qJars">
366		<cfscript>
367			libName = ListGetAt(name, 1, "-");
368			//let's not use the lib's that have the same name, but a lower datestamp
369			if(NOT ListFind(jarList, libName))
370			{
371				ArrayAppend(aJars, path & "/" & name);
372				jarList = ListAppend(jarList, libName);
373			}
374		</cfscript>
375	</cfloop>
376
377	<cfreturn aJars>
378</cffunction>
379
380<cffunction name="getClassLoadPaths" access="private" returntype="array" output="false">
381	<cfreturn instance.classLoadPaths />
382</cffunction>
383
384<cffunction name="setClassLoadPaths" access="private" returntype="void" output="false">
385	<cfargument name="classLoadPaths" type="array" required="true">
386	<cfset instance.classLoadPaths = arguments.classLoadPaths />
387</cffunction>
388
389<cffunction name="getParentClassLoader" access="private" returntype="any" output="false">
390	<cfreturn instance.parentClassLoader />
391</cffunction>
392
393<cffunction name="setParentClassLoader" access="private" returntype="void" output="false">
394	<cfargument name="parentClassLoader" type="any" required="true">
395	<cfset instance.parentClassLoader = arguments.parentClassLoader />
396</cffunction>
397
398<cffunction name="getServerURLClassLoader" hint="returns the server URL class loader" access="private" returntype="any" output="false">
399	<cfreturn server[instance.static.uuid & "." & getVersion()] />
400</cffunction>
401
402<cffunction name="setURLClassLoader" access="private" returntype="void" output="false">
403	<cfargument name="ClassLoader" type="any" required="true">
404	<cfset instance.ClassLoader = arguments.ClassLoader />
405</cffunction>
406
407<cffunction name="hasJavaCompiler" hint="whether this object has a javaCompiler" access="private" returntype="boolean" output="false">
408	<cfreturn StructKeyExists(instance, "javaCompiler") />
409</cffunction>
410
411<cffunction name="getJavaCompiler" access="private" returntype="JavaCompiler" output="false">
412	<cfreturn instance.javaCompiler />
413</cffunction>
414
415<cffunction name="setJavaCompiler" access="private" returntype="void" output="false">
416	<cfargument name="javaCompiler" type="JavaCompiler" required="true">
417	<cfset instance.javaCompiler = arguments.javaCompiler />
418</cffunction>
419
420<cffunction name="getSourceDirectories" access="private" returntype="array" output="false">
421	<cfreturn instance.sourceDirectories />
422</cffunction>
423
424<cffunction name="setSourceDirectories" access="private" returntype="void" output="false">
425	<cfargument name="sourceDirectories" type="array" required="true">
426	<cfset instance.sourceDirectories = arguments.sourceDirectories />
427</cffunction>
428
429<cffunction name="getSourceLastModified" access="private" returntype="date" output="false">
430	<cfreturn instance.sourceLastModified />
431</cffunction>
432
433<cffunction name="setSourceLastModified" access="private" returntype="void" output="false">
434	<cfargument name="sourceLastModified" type="date" required="true">
435	<cfset instance.sourceLastModified = arguments.sourceLastModified />
436</cffunction>
437
438<cffunction name="hasSourceLastModified" hint="whether this object has a sourceLastModified" access="private" returntype="boolean" output="false">
439	<cfreturn StructKeyExists(instance, "sourceLastModified") />
440</cffunction>
441
442<cffunction name="getCompileDirectory" access="private" returntype="string" output="false">
443	<cfreturn instance.compileDirectory />
444</cffunction>
445
446<cffunction name="setCompileDirectory" access="private" returntype="void" output="false">
447	<cfargument name="compileDirectory" type="string" required="true">
448	<cfset instance.compileDirectory = arguments.compileDirectory />
449</cffunction>
450
451<cffunction name="throwException" access="private" hint="Throws an Exception" output="false">
452	<cfargument name="type" hint="The type of exception" type="string" required="Yes">
453	<cfargument name="message" hint="The message to accompany the exception" type="string" required="Yes">
454	<cfargument name="detail" type="string" hint="The detail message for the exception" required="No" default="">
455		<cfthrow type="#arguments.type#" message="#arguments.message#" detail="#arguments.detail#">
456</cffunction>
457
458<cffunction name="isTrustedSource" access="private" returntype="boolean" output="false">
459	<cfreturn instance.isTrustedSource />
460</cffunction>
461
462<cffunction name="setTrustedSource" access="private" returntype="void" output="false">
463	<cfargument name="isTrustedSource" type="boolean" required="true">
464	<cfset instance.isTrustedSource = arguments.isTrustedSource />
465</cffunction>
466
467<!---
468Copies a directory.
469
470@param source      Source directory. (Required)
471@param destination      Destination directory. (Required)
472@param nameConflict      What to do when a conflict occurs (skip, overwrite, makeunique). Defaults to overwrite. (Optional)
473@return Returns nothing.
474@author Joe Rinehart (joe.rinehart@gmail.com)
475@version 1, July 27, 2005
476--->
477<cffunction name="directoryCopy" access="private" output="true">
478    <cfargument name="source" required="true" type="string">
479    <cfargument name="destination" required="true" type="string">
480    <cfargument name="nameconflict" required="true" default="overwrite">
481
482    <cfset var contents = "" />
483    <cfset var dirDelim = createObject("java", "java.lang.System").getProperty("file.separator")>
484
485    <cfif not(directoryExists(arguments.destination))>
486        <cfdirectory action="create" directory="#arguments.destination#">
487    </cfif>
488
489    <cfdirectory action="list" directory="#arguments.source#" name="contents">
490
491    <cfloop query="contents">
492        <cfif contents.type eq "file">
493            <cffile action="copy" source="#arguments.source#/#name#" destination="#arguments.destination#/#name#" nameconflict="#arguments.nameConflict#">
494        <cfelseif contents.type eq "dir">
495            <cfset directoryCopy(arguments.source & dirDelim & name, arguments.destination & dirDelim & name) />
496        </cfif>
497    </cfloop>
498</cffunction>
499
500</cfcomponent>