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
|
// SPDX-License-Identifier: BSD-3-Clause
package org.islandoftex.arara.utils
import java.io.File
import java.lang.reflect.InvocationTargetException
import java.net.MalformedURLException
import java.net.URLClassLoader
/**
* Implements utilitary methods for classloading and object instantiation.
*
* @author Island of TeX
* @version 5.0
* @since 4.0
*/
object ClassLoadingUtils {
/**
* Indicator of success or failure of class loading.
*/
enum class ClassLoadingStatus {
SUCCESS,
FILE_NOT_FOUND,
MALFORMED_URL,
CLASS_NOT_FOUND,
ILLEGAL_ACCESS,
INSTANTIATION_EXCEPTION
}
/**
* Loads a class from the provided file, potentially a Java archive.
* @param file File containing the Java bytecode (namely, a JAR).
* @param name The canonical name of the class.
* @return A pair representing the status and the class.
*/
fun loadClass(file: File, name: String):
Pair<ClassLoadingStatus, Class<*>> {
// status and class to be returned,
// it defaults to an object class
var value: Class<*> = Any::class.java
// if file does not exist, nothing
// can be done, status is changed
val status = if (!file.exists()) {
ClassLoadingStatus.FILE_NOT_FOUND
} else {
// classloading involves defining
// a classloader and fetching the
// desired class from it, based on
// the provided file archive
try {
// creates a new classloader with
// the provided file (potentially
// a JAR file)
val classloader = URLClassLoader(arrayOf(file.toURI().toURL()),
ClassLoadingUtils::class.java.classLoader)
// fetches the class from the
// instantiated classloader
value = Class.forName(name, true, classloader)
ClassLoadingStatus.SUCCESS
} catch (_: MalformedURLException) {
ClassLoadingStatus.MALFORMED_URL
} catch (_: ClassNotFoundException) {
ClassLoadingStatus.CLASS_NOT_FOUND
}
}
// return a new pair based on the
// current status and class holder
return status to value
}
/**
* Loads a class from the provided file, instantiating it.
* @param file File containing the Java bytecode (namely, a JAR).
* @param name The canonical name of the class.
* @return A pair representing the status and the class object.
*/
fun loadObject(file: File, name: String): Pair<ClassLoadingStatus, Any> {
// load the corresponding class
// based on the qualified name
val pair = loadClass(file, name)
// status and object to be returned,
// it defaults to an object
var status = pair.first
var value = Any()
// checks if the class actually
// exists, otherwise simply
// ignore instantiation
if (status == ClassLoadingStatus.SUCCESS) {
// object instantiation relies
// on the default constructor
// (without arguments), class
// must implement it
// OBS: constructors with arguments
// must be invoked through reflection
try {
// get the class reference from
// the pair and instantiate it
// by invoking the default
// constructor (without arguments)
value = pair.second.getDeclaredConstructor().newInstance()
} catch (_: IllegalAccessException) {
status = ClassLoadingStatus.ILLEGAL_ACCESS
} catch (_: InstantiationException) {
// the user wanted to instantiate an abstract class
status = ClassLoadingStatus.INSTANTIATION_EXCEPTION
} catch (_: InvocationTargetException) {
// the underlying constructor caused an exception
status = ClassLoadingStatus.INSTANTIATION_EXCEPTION
}
}
// return a new pair based on the
// current status and object holder
return status to value
}
}
|