embulk 0.8.25-java → 0.8.26-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/build.gradle +16 -1
- data/embulk-cli/src/main/java/org/embulk/cli/EmbulkMigrate.java +6 -3
- data/embulk-core/build.gradle +7 -0
- data/embulk-core/src/main/java/org/embulk/EmbulkService.java +2 -0
- data/embulk-core/src/main/java/org/embulk/plugin/InjectedPluginSource.java +1 -1
- data/embulk-core/src/main/java/org/embulk/plugin/MavenPluginType.java +21 -6
- data/embulk-core/src/main/java/org/embulk/plugin/PluginClassLoader.java +42 -2
- data/embulk-core/src/main/java/org/embulk/plugin/PluginSourceNotMatchException.java +6 -0
- data/embulk-core/src/main/java/org/embulk/plugin/PluginType.java +2 -1
- data/embulk-core/src/main/java/org/embulk/plugin/jar/InvalidJarPluginException.java +14 -0
- data/embulk-core/src/main/java/org/embulk/plugin/jar/JarPluginLoader.java +205 -0
- data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenArtifactFinder.java +134 -0
- data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenArtifactNotFoundException.java +20 -0
- data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenPluginSource.java +187 -0
- data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenPluginSourceModule.java +22 -0
- data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenRepositoryNotFoundException.java +31 -0
- data/embulk-core/src/main/resources/embulk/parent_first_packages.properties +3 -1
- data/embulk-core/src/main/resources/embulk/parent_first_resources.properties +2 -1
- data/embulk-core/src/test/java/org/embulk/EmbulkTestRuntime.java +8 -0
- data/embulk-core/src/test/java/org/embulk/plugin/TestPluginType.java +24 -0
- data/embulk-core/src/test/java/org/embulk/plugin/TestPluginTypeSerDe.java +17 -0
- data/embulk-core/src/test/java/org/embulk/plugin/jar/ExampleJarSpiV0.java +9 -0
- data/embulk-core/src/test/java/org/embulk/plugin/jar/JarBuilder.java +101 -0
- data/embulk-core/src/test/java/org/embulk/plugin/jar/TestJarPluginLoader.java +60 -0
- data/embulk-core/src/test/java/org/embulk/plugin/maven/TestMavenArtifactFinder.java +41 -0
- data/embulk-core/src/test/resources/m2.test/.gitignore +1 -0
- data/embulk-core/src/test/resources/m2.test/org/embulk/example/embulk-example-maven-artifact/0.1.2/embulk-example-maven-artifact-0.1.2.jar +0 -0
- data/embulk-core/src/test/resources/m2.test/org/embulk/example/embulk-example-maven-artifact/0.1.2/embulk-example-maven-artifact-0.1.2.jar.sha1 +1 -0
- data/embulk-core/src/test/resources/m2.test/org/embulk/example/embulk-example-maven-artifact/0.1.2/embulk-example-maven-artifact-0.1.2.pom +9 -0
- data/embulk-core/src/test/resources/m2.test/org/embulk/example/embulk-example-maven-artifact/0.1.2/embulk-example-maven-artifact-0.1.2.pom.sha1 +1 -0
- data/embulk-docs/src/built-in.rst +26 -9
- data/embulk-docs/src/release.rst +1 -0
- data/embulk-docs/src/release/release-0.8.26.rst +16 -0
- data/embulk-standards/src/main/java/org/embulk/standards/ConfigInputPlugin.java +170 -0
- data/embulk-standards/src/main/java/org/embulk/standards/StandardPluginModule.java +1 -0
- data/lib/embulk/version.rb +1 -1
- metadata +58 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67e9c5003f422b52f56fd7347f77dda2ea07d86e
|
4
|
+
data.tar.gz: ac13f8e09bcc9998ba716fc96e6e98e0546605ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a275c7189877da26bed04a116dab321b0bf5d9c8f378143aa743c2fa05ae0806fb8d27eabaad76c6bdd7b06971a1b8f5ea2aa8a52152aa7e67a129545e417bc
|
7
|
+
data.tar.gz: 6ce56d2cfec57fc2db846f7be7aaf5209e30ac5180b28f0d9930f7cebd333f614e443dcb1e57b49bc58683e4b1a595239809bbe50d4915f9bd21e5d59597abb7
|
data/build.gradle
CHANGED
@@ -16,7 +16,7 @@ def release_projects = [project(":embulk-core"), project(":embulk-standards"), p
|
|
16
16
|
|
17
17
|
allprojects {
|
18
18
|
group = 'org.embulk'
|
19
|
-
version = '0.8.
|
19
|
+
version = '0.8.26'
|
20
20
|
|
21
21
|
ext {
|
22
22
|
jrubyVersion = '9.1.5.0'
|
@@ -100,6 +100,9 @@ subprojects {
|
|
100
100
|
tasks.withType(JavaCompile) {
|
101
101
|
options.compilerArgs << "-Xlint:unchecked" //<< "-Xlint:deprecation"
|
102
102
|
}
|
103
|
+
tasks.withType(Test) {
|
104
|
+
systemProperties System.properties.findAll { it.key.startsWith("org.embulk") }
|
105
|
+
}
|
103
106
|
tasks.withType(FindBugs) {
|
104
107
|
reports {
|
105
108
|
xml.enabled = false
|
@@ -227,6 +230,18 @@ project(':embulk-cli') {
|
|
227
230
|
apply plugin: 'com.github.johnrengelman.shadow'
|
228
231
|
|
229
232
|
shadowJar {
|
233
|
+
// 'org.apache.maven' is used in:
|
234
|
+
// - embulk-cli: EmbulkMigrate and EmbulkSelfUpdate (from v0.8.17)
|
235
|
+
// - embulk-core: MavenPluginSource and the related (from v0.8.26)
|
236
|
+
relocate 'org.apache.maven', 'org.embulk.third_party.org.apache.maven'
|
237
|
+
|
238
|
+
// 'org.codehaus.plexus' is used from 'org.apache.maven'.
|
239
|
+
relocate 'org.codehaus.plexus', 'org.embulk.third_party.org.codehaus.plexus'
|
240
|
+
|
241
|
+
// 'org.eclipse.aether' is used in:
|
242
|
+
// - embulk-core: MavenPluginSource and the related (from v0.8.26)
|
243
|
+
relocate 'org.eclipse.aether', 'org.embulk.third_party.org.eclipse.aether'
|
244
|
+
|
230
245
|
// NOTE: This 'Implementation-Version' in the manifest is referred to provide the Embulk version at runtime.
|
231
246
|
// See also: embulk-core/src/main/java/org/embulk/EmbulkVersion.java
|
232
247
|
manifest {
|
@@ -10,6 +10,7 @@ import java.nio.file.Path;
|
|
10
10
|
import java.nio.file.PathMatcher;
|
11
11
|
import java.nio.file.Paths;
|
12
12
|
import java.nio.file.SimpleFileVisitor;
|
13
|
+
import java.nio.file.StandardCopyOption;
|
13
14
|
import java.nio.file.StandardOpenOption;
|
14
15
|
import java.nio.file.attribute.BasicFileAttributes;
|
15
16
|
import java.util.ArrayList;
|
@@ -76,7 +77,7 @@ public class EmbulkMigrate
|
|
76
77
|
System.out.println("Done. No files are modified.");
|
77
78
|
}
|
78
79
|
else {
|
79
|
-
System.out.println("Done. Please check
|
80
|
+
System.out.println("Done. Please check modified files.");
|
80
81
|
}
|
81
82
|
}
|
82
83
|
|
@@ -236,7 +237,9 @@ public class EmbulkMigrate
|
|
236
237
|
{
|
237
238
|
Path destinationPath = this.basePath.resolve(destinationFileName);
|
238
239
|
Files.createDirectories(destinationPath.getParent());
|
239
|
-
Files.copy(EmbulkMigrate.class.getClassLoader().getResourceAsStream(sourceResourcePath), destinationPath
|
240
|
+
Files.copy(EmbulkMigrate.class.getClassLoader().getResourceAsStream(sourceResourcePath), destinationPath,
|
241
|
+
StandardCopyOption.REPLACE_EXISTING);
|
242
|
+
this.modifiedFiles.add(destinationPath);
|
240
243
|
}
|
241
244
|
|
242
245
|
public boolean match(String filePath, Pattern pattern)
|
@@ -424,7 +427,7 @@ public class EmbulkMigrate
|
|
424
427
|
private static final Pattern OLD_EMBULK_IN_GEMSPEC = Pattern.compile(
|
425
428
|
"embulk-");
|
426
429
|
private static final Pattern GRADLE_VERSION_IN_WRAPPER = Pattern.compile(
|
427
|
-
"gradle-[23]\\.\\d+(\\.\\d+)
|
430
|
+
"gradle-[23]\\.\\d+(\\.\\d+)?-");
|
428
431
|
private static final Pattern JSON_COLUMN_METHOD_IN_ALL_JAVA = Pattern.compile(
|
429
432
|
"void\\s+jsonColumn");
|
430
433
|
private static final Pattern TIMESTAMP_COLUMN_METHOD_IN_ALL_JAVA = Pattern.compile(
|
data/embulk-core/build.gradle
CHANGED
@@ -43,6 +43,13 @@ dependencies {
|
|
43
43
|
// For embulk/guess/charset.rb. See also embulk.gemspec
|
44
44
|
compile 'com.ibm.icu:icu4j:54.1.1'
|
45
45
|
|
46
|
+
// For MavenPluginSource / MavenArtifactFinder.
|
47
|
+
compile 'org.eclipse.aether:aether-api:1.1.0'
|
48
|
+
compile 'org.eclipse.aether:aether-spi:1.1.0'
|
49
|
+
compile 'org.eclipse.aether:aether-util:1.1.0'
|
50
|
+
compile 'org.eclipse.aether:aether-impl:1.1.0'
|
51
|
+
compile 'org.apache.maven:maven-aether-provider:3.3.9'
|
52
|
+
|
46
53
|
gems 'rubygems:bundler:1.10.6'
|
47
54
|
gems 'rubygems:msgpack:1.1.0'
|
48
55
|
gems 'rubygems:liquid:3.0.6'
|
@@ -11,6 +11,7 @@ import org.embulk.exec.ExecModule;
|
|
11
11
|
import org.embulk.exec.ExtensionServiceLoaderModule;
|
12
12
|
import org.embulk.plugin.PluginClassLoaderModule;
|
13
13
|
import org.embulk.plugin.BuiltinPluginSourceModule;
|
14
|
+
import org.embulk.plugin.maven.MavenPluginSourceModule;
|
14
15
|
import org.embulk.jruby.JRubyScriptingModule;
|
15
16
|
import static com.google.common.base.Preconditions.checkState;
|
16
17
|
|
@@ -45,6 +46,7 @@ public class EmbulkService
|
|
45
46
|
new ExtensionServiceLoaderModule(systemConfig),
|
46
47
|
new PluginClassLoaderModule(systemConfig),
|
47
48
|
new BuiltinPluginSourceModule(),
|
49
|
+
new MavenPluginSourceModule(systemConfig),
|
48
50
|
new JRubyScriptingModule(systemConfig));
|
49
51
|
}
|
50
52
|
|
@@ -10,10 +10,11 @@ import java.util.Objects;
|
|
10
10
|
public final class MavenPluginType
|
11
11
|
extends PluginType
|
12
12
|
{
|
13
|
-
private MavenPluginType(final String name, final String group, final String version)
|
13
|
+
private MavenPluginType(final String name, final String group, final String classifier, final String version)
|
14
14
|
{
|
15
15
|
super("maven", name);
|
16
16
|
this.group = group;
|
17
|
+
this.classifier = classifier;
|
17
18
|
this.version = version;
|
18
19
|
|
19
20
|
final StringBuilder fullNameBuilder = new StringBuilder();
|
@@ -23,6 +24,10 @@ public final class MavenPluginType
|
|
23
24
|
fullNameBuilder.append(name);
|
24
25
|
fullNameBuilder.append(":");
|
25
26
|
fullNameBuilder.append(version);
|
27
|
+
if (classifier != null) {
|
28
|
+
fullNameBuilder.append(":");
|
29
|
+
fullNameBuilder.append(classifier);
|
30
|
+
}
|
26
31
|
this.fullName = fullNameBuilder.toString();
|
27
32
|
|
28
33
|
final HashMap<String, String> fullMapMutable = new HashMap<String, String>();
|
@@ -33,12 +38,13 @@ public final class MavenPluginType
|
|
33
38
|
this.fullMap = Collections.unmodifiableMap(fullMapMutable);
|
34
39
|
}
|
35
40
|
|
36
|
-
public static MavenPluginType create(
|
41
|
+
public static MavenPluginType create(
|
42
|
+
final String name, final String group, final String classifier, final String version)
|
37
43
|
{
|
38
|
-
if (name == null) {
|
39
|
-
throw new NullPointerException("\"name\" must
|
44
|
+
if (name == null || group == null || version == null) {
|
45
|
+
throw new NullPointerException("\"name\", \"group\" and \"version\" must be present.");
|
40
46
|
}
|
41
|
-
return new MavenPluginType(name, group, version);
|
47
|
+
return new MavenPluginType(name, group, classifier, version);
|
42
48
|
}
|
43
49
|
|
44
50
|
@JsonValue
|
@@ -53,6 +59,12 @@ public final class MavenPluginType
|
|
53
59
|
return this.group;
|
54
60
|
}
|
55
61
|
|
62
|
+
@JsonProperty("classifier")
|
63
|
+
public final String getClassifier()
|
64
|
+
{
|
65
|
+
return this.classifier;
|
66
|
+
}
|
67
|
+
|
56
68
|
@JsonProperty("version")
|
57
69
|
public final String getVersion()
|
58
70
|
{
|
@@ -67,7 +79,7 @@ public final class MavenPluginType
|
|
67
79
|
@Override
|
68
80
|
public final int hashCode()
|
69
81
|
{
|
70
|
-
return Objects.hash(getSourceType(), getName(), this.group, this.version);
|
82
|
+
return Objects.hash(getSourceType(), getName(), this.group, this.classifier, this.version);
|
71
83
|
}
|
72
84
|
|
73
85
|
@Override
|
@@ -80,6 +92,8 @@ public final class MavenPluginType
|
|
80
92
|
return (this.getSourceType().equals(other.getSourceType()) &&
|
81
93
|
this.getName().equals(other.getName()) &&
|
82
94
|
this.getGroup().equals(other.getGroup()) &&
|
95
|
+
((this.getClassifier() == null && other.getClassifier() == null) ||
|
96
|
+
(this.getClassifier() != null && this.getClassifier().equals(other.getClassifier()))) &&
|
83
97
|
this.getVersion().equals(other.getVersion()));
|
84
98
|
}
|
85
99
|
|
@@ -91,6 +105,7 @@ public final class MavenPluginType
|
|
91
105
|
}
|
92
106
|
|
93
107
|
private final String group;
|
108
|
+
private final String classifier; // |classifier| can be null.
|
94
109
|
private final String version;
|
95
110
|
private final String fullName;
|
96
111
|
private final Map<String, String> fullMap;
|
@@ -42,6 +42,15 @@ public class PluginClassLoader
|
|
42
42
|
}));
|
43
43
|
}
|
44
44
|
|
45
|
+
/**
|
46
|
+
* Adds the specified path to the list of URLs (for {@code URLClassLoader}) to search for classes and resources.
|
47
|
+
*
|
48
|
+
* It internally calls {@code URLClassLoader#addURL}.
|
49
|
+
*
|
50
|
+
* Some plugins (embulk-input-jdbc, for example) are calling this method to load external JAR files.
|
51
|
+
*
|
52
|
+
* @see https://github.com/embulk/embulk-input-jdbc/blob/ebfff0b249d507fc730c87e08b56e6aa492060ca/embulk-input-jdbc/src/main/java/org/embulk/input/jdbc/AbstractJdbcInputPlugin.java#L586-L595
|
53
|
+
*/
|
45
54
|
public void addPath(Path path)
|
46
55
|
{
|
47
56
|
try {
|
@@ -56,17 +65,45 @@ public class PluginClassLoader
|
|
56
65
|
super.addURL(url);
|
57
66
|
}
|
58
67
|
|
68
|
+
/**
|
69
|
+
* Loads the class with the specified binary name prioritized by the "parent-first" condition.
|
70
|
+
*
|
71
|
+
* It copy-cats {@code ClassLoader#loadClass} while the "parent-first" priorities are considered.
|
72
|
+
*
|
73
|
+
* If the specified class is "parent-first", it behaves the same as {@code ClassLoader#loadClass} ordered as below.
|
74
|
+
*
|
75
|
+
* <ol>
|
76
|
+
*
|
77
|
+
* <li><p>Invoke the {@code #findLoadedClass} method to check if the class has already been loaded.</p></li>
|
78
|
+
*
|
79
|
+
* <li><p>Invoke the parent's {@code #loadClass} method.
|
80
|
+
*
|
81
|
+
* <li><p>Invoke the {@code #findClass} method of this class loader to find the class.</p></li>
|
82
|
+
*
|
83
|
+
* </ol>
|
84
|
+
*
|
85
|
+
* If the specified class is "NOT parent-first", the 2nd and 3rd actions are swapped.
|
86
|
+
*
|
87
|
+
* @see https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String,%20boolean)
|
88
|
+
* @see http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u141-b02/src/share/classes/java/lang/ClassLoader.java
|
89
|
+
*/
|
59
90
|
@Override
|
60
91
|
protected Class<?> loadClass(String name, boolean resolve)
|
61
92
|
throws ClassNotFoundException
|
62
93
|
{
|
63
94
|
synchronized (getClassLoadingLock(name)) {
|
64
|
-
|
95
|
+
// If the class has already been loaded by this {@code ClassLoader} or the parent's {@code ClassLoader},
|
96
|
+
// find the loaded class and return it.
|
97
|
+
final Class<?> loadedClass = findLoadedClass(name);
|
98
|
+
|
65
99
|
if (loadedClass != null) {
|
66
100
|
return resolveClass(loadedClass, resolve);
|
67
101
|
}
|
68
102
|
|
69
|
-
boolean parentFirst = isParentFirstPackage(name);
|
103
|
+
final boolean parentFirst = isParentFirstPackage(name);
|
104
|
+
|
105
|
+
// If the class is "not parent-first" (not to be loaded by the parent at first),
|
106
|
+
// try {@code #findClass} of the child's ({@code PluginClassLoader}'s).
|
70
107
|
if (!parentFirst) {
|
71
108
|
try {
|
72
109
|
return resolveClass(findClass(name), resolve);
|
@@ -74,11 +111,14 @@ public class PluginClassLoader
|
|
74
111
|
}
|
75
112
|
}
|
76
113
|
|
114
|
+
// If the class is "parent-first" (to be loaded by the parent at first), try this part at first.
|
115
|
+
// If the class is "not parent-first" (not to be loaded by the parent at first), the above part runs first.
|
77
116
|
try {
|
78
117
|
return resolveClass(getParent().loadClass(name), resolve);
|
79
118
|
} catch (ClassNotFoundException ignored) {
|
80
119
|
}
|
81
120
|
|
121
|
+
// If the class is "parent-first" (to be loaded by the parent at first), this part runs after the above.
|
82
122
|
if (parentFirst) {
|
83
123
|
return resolveClass(findClass(name), resolve);
|
84
124
|
}
|
@@ -5,6 +5,7 @@ public class PluginSourceNotMatchException
|
|
5
5
|
{
|
6
6
|
public PluginSourceNotMatchException()
|
7
7
|
{
|
8
|
+
super();
|
8
9
|
}
|
9
10
|
|
10
11
|
public PluginSourceNotMatchException(String message)
|
@@ -16,4 +17,9 @@ public class PluginSourceNotMatchException
|
|
16
17
|
{
|
17
18
|
super(cause);
|
18
19
|
}
|
20
|
+
|
21
|
+
public PluginSourceNotMatchException(String message, Throwable cause)
|
22
|
+
{
|
23
|
+
super(message, cause);
|
24
|
+
}
|
19
25
|
}
|
@@ -79,8 +79,9 @@ public abstract class PluginType
|
|
79
79
|
{
|
80
80
|
final String name = stringMap.get("name");
|
81
81
|
final String group = stringMap.get("group");
|
82
|
+
final String classifier = stringMap.get("classifier");
|
82
83
|
final String version = stringMap.get("version");
|
83
|
-
return MavenPluginType.create(name, group, version);
|
84
|
+
return MavenPluginType.create(name, group, classifier, version);
|
84
85
|
}
|
85
86
|
default:
|
86
87
|
throw new IllegalArgumentException("\"source\" must be one of: [\"default\", \"maven\"]");
|
@@ -0,0 +1,14 @@
|
|
1
|
+
package org.embulk.plugin.jar;
|
2
|
+
|
3
|
+
public class InvalidJarPluginException
|
4
|
+
extends Exception {
|
5
|
+
public InvalidJarPluginException(final String message)
|
6
|
+
{
|
7
|
+
super(message);
|
8
|
+
}
|
9
|
+
|
10
|
+
public InvalidJarPluginException(final String message, final Throwable cause)
|
11
|
+
{
|
12
|
+
super(message, cause);
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,205 @@
|
|
1
|
+
package org.embulk.plugin.jar;
|
2
|
+
|
3
|
+
import com.google.common.collect.ImmutableList;
|
4
|
+
import java.io.IOError;
|
5
|
+
import java.io.IOException;
|
6
|
+
import java.net.JarURLConnection;
|
7
|
+
import java.net.MalformedURLException;
|
8
|
+
import java.net.URI;
|
9
|
+
import java.net.URL;
|
10
|
+
import java.nio.file.Path;
|
11
|
+
import java.util.jar.Attributes;
|
12
|
+
import java.util.jar.Manifest;
|
13
|
+
import org.embulk.plugin.PluginClassLoader;
|
14
|
+
import org.embulk.plugin.PluginClassLoaderFactory;
|
15
|
+
|
16
|
+
/**
|
17
|
+
* JarPluginLoader loads a JAR-based Embulk plugin class.
|
18
|
+
*
|
19
|
+
* It implements {@code AutoCloseable} so that it can load the contents lazily in the future.
|
20
|
+
* It is recommended to try-with-resources to use this class.
|
21
|
+
*/
|
22
|
+
public class JarPluginLoader
|
23
|
+
implements AutoCloseable
|
24
|
+
{
|
25
|
+
public static final String MANIFEST_PLUGIN_MAIN_CLASS = "Embulk-Plugin-Main-Class";
|
26
|
+
public static final String MANIFEST_PLUGIN_SPI_VERSION = "Embulk-Plugin-Spi-Version";
|
27
|
+
|
28
|
+
private JarPluginLoader(final Manifest pluginManifest,
|
29
|
+
final Attributes pluginManifestAttributes,
|
30
|
+
final Class pluginMainClass)
|
31
|
+
{
|
32
|
+
this.pluginManifest = pluginManifest;
|
33
|
+
this.pluginManifestAttributes = pluginManifestAttributes;
|
34
|
+
this.pluginMainClass = pluginMainClass;
|
35
|
+
}
|
36
|
+
|
37
|
+
public static JarPluginLoader load(final Path jarPath, final PluginClassLoaderFactory classLoaderFactory)
|
38
|
+
throws InvalidJarPluginException
|
39
|
+
{
|
40
|
+
final JarURLConnection connection = openJarURLConnection(jarPath);
|
41
|
+
final Manifest manifest = loadJarPluginManifest(connection, jarPath);
|
42
|
+
final Attributes manifestAttributes = manifest.getMainAttributes();
|
43
|
+
final int spiVersion = getPluginSpiVersionFromManifest(manifestAttributes);
|
44
|
+
|
45
|
+
if (spiVersion == 0) {
|
46
|
+
final String mainClassName = getPluginMainClassNameFromManifest(manifestAttributes);
|
47
|
+
final Class mainClass = loadJarPluginMainClass(jarPath, mainClassName, classLoaderFactory);
|
48
|
+
return new JarPluginLoader(manifest, manifestAttributes, mainClass);
|
49
|
+
}
|
50
|
+
|
51
|
+
throw new InvalidJarPluginException("Unknown SPI version of JAR plugin: " + spiVersion);
|
52
|
+
}
|
53
|
+
|
54
|
+
public Class getPluginMainClass()
|
55
|
+
throws InvalidJarPluginException
|
56
|
+
{
|
57
|
+
return this.pluginMainClass;
|
58
|
+
}
|
59
|
+
|
60
|
+
public int getPluginSpiVersion()
|
61
|
+
throws InvalidJarPluginException
|
62
|
+
{
|
63
|
+
return getPluginSpiVersionFromManifest(this.pluginManifestAttributes);
|
64
|
+
}
|
65
|
+
|
66
|
+
@Override
|
67
|
+
public void close()
|
68
|
+
throws InvalidJarPluginException
|
69
|
+
{
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Opens JarURLConnection for the given JAR file path.
|
74
|
+
*
|
75
|
+
* Note that JarURLConnection does not have {@code disconnect} nor {@code close}.
|
76
|
+
*/
|
77
|
+
private static JarURLConnection openJarURLConnection(final Path jarPath)
|
78
|
+
throws InvalidJarPluginException
|
79
|
+
{
|
80
|
+
// jar:file:/...
|
81
|
+
final URL jarUrl;
|
82
|
+
try {
|
83
|
+
jarUrl = new URL("jar:" + jarPath.toUri().toURL().toString() + "!/");
|
84
|
+
}
|
85
|
+
catch (MalformedURLException ex) {
|
86
|
+
throw new InvalidJarPluginException("JAR plugin path specified is invalid: " + jarPath.toString(), ex);
|
87
|
+
}
|
88
|
+
|
89
|
+
try {
|
90
|
+
return (JarURLConnection) jarUrl.openConnection();
|
91
|
+
}
|
92
|
+
catch (IOException ex) {
|
93
|
+
throw new InvalidJarPluginException("JAR plugin specified is formatted wrongly: " + jarPath.toString(), ex);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
private static Manifest loadJarPluginManifest(final JarURLConnection connection, final Path jarPath)
|
98
|
+
throws InvalidJarPluginException
|
99
|
+
{
|
100
|
+
try {
|
101
|
+
return connection.getManifest();
|
102
|
+
}
|
103
|
+
catch (IOException ex) {
|
104
|
+
throw new InvalidJarPluginException("Manifest in JAR plugin specified is invalid: " + jarPath.toString(),
|
105
|
+
ex);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
private static Class loadJarPluginMainClass(final Path jarPath,
|
110
|
+
final String pluginMainClassName,
|
111
|
+
final PluginClassLoaderFactory pluginClassLoaderFactory)
|
112
|
+
throws InvalidJarPluginException
|
113
|
+
{
|
114
|
+
final URI fileUriJar;
|
115
|
+
try {
|
116
|
+
fileUriJar = jarPath.toUri();
|
117
|
+
}
|
118
|
+
catch (IOError ex) {
|
119
|
+
throw new InvalidJarPluginException("[FATAL] JAR plugin path specified is invalid: " + jarPath.toString(),
|
120
|
+
ex);
|
121
|
+
}
|
122
|
+
catch (SecurityException ex) {
|
123
|
+
throw new InvalidJarPluginException("Security manager prohibits getting the working directory: " +
|
124
|
+
jarPath.toString() +
|
125
|
+
". Specifying an absolute path for JAR plugin may solve this.",
|
126
|
+
ex);
|
127
|
+
}
|
128
|
+
|
129
|
+
// file:/...
|
130
|
+
final URL fileUrlJar;
|
131
|
+
try {
|
132
|
+
fileUrlJar = fileUriJar.toURL();
|
133
|
+
}
|
134
|
+
catch (IllegalArgumentException ex) {
|
135
|
+
throw new InvalidJarPluginException("[FATAL/INTERNAL] JAR plugin path as URI is not absolute.", ex);
|
136
|
+
}
|
137
|
+
catch (MalformedURLException ex) {
|
138
|
+
throw new InvalidJarPluginException("JAR plugin path specified is invalid: " + jarPath.toString(), ex);
|
139
|
+
}
|
140
|
+
|
141
|
+
|
142
|
+
final PluginClassLoader pluginClassLoader =
|
143
|
+
pluginClassLoaderFactory.create(ImmutableList.of(fileUrlJar), JarPluginLoader.class.getClassLoader());
|
144
|
+
|
145
|
+
final Class pluginMainClass;
|
146
|
+
try {
|
147
|
+
pluginMainClass = pluginClassLoader.loadClass(pluginMainClassName);
|
148
|
+
}
|
149
|
+
catch (ClassNotFoundException ex) {
|
150
|
+
throw new InvalidJarPluginException("Class " + pluginMainClassName + " not found in " + jarPath.toString(),
|
151
|
+
ex);
|
152
|
+
}
|
153
|
+
|
154
|
+
return pluginMainClass;
|
155
|
+
}
|
156
|
+
|
157
|
+
private static int getPluginSpiVersionFromManifest(final Attributes manifestAttributes)
|
158
|
+
throws InvalidJarPluginException
|
159
|
+
{
|
160
|
+
final String spiVersionString = getAttributeFromManifest(manifestAttributes, MANIFEST_PLUGIN_SPI_VERSION);
|
161
|
+
|
162
|
+
if (spiVersionString == null) {
|
163
|
+
throw new InvalidJarPluginException("SPI version of JAR plugin is not specified.");
|
164
|
+
}
|
165
|
+
|
166
|
+
try {
|
167
|
+
return Integer.parseInt(spiVersionString);
|
168
|
+
}
|
169
|
+
catch (NumberFormatException ex) {
|
170
|
+
throw new InvalidJarPluginException("SPI version of JAR plugin is not an integer: \"" +
|
171
|
+
spiVersionString +
|
172
|
+
"\"", ex);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
private static String getPluginMainClassNameFromManifest(final Attributes manifestAttributes)
|
177
|
+
throws InvalidJarPluginException
|
178
|
+
{
|
179
|
+
final String pluginMainClassName = getAttributeFromManifest(manifestAttributes, MANIFEST_PLUGIN_MAIN_CLASS);
|
180
|
+
|
181
|
+
if (pluginMainClassName == null) {
|
182
|
+
throw new InvalidJarPluginException("Main class name of JAR plugin is not specified.");
|
183
|
+
}
|
184
|
+
|
185
|
+
return pluginMainClassName;
|
186
|
+
}
|
187
|
+
|
188
|
+
private static String getAttributeFromManifest(final Attributes manifestAttributes, final String attributeName)
|
189
|
+
throws InvalidJarPluginException
|
190
|
+
{
|
191
|
+
try {
|
192
|
+
return manifestAttributes.getValue(attributeName);
|
193
|
+
}
|
194
|
+
catch (IllegalArgumentException ex) {
|
195
|
+
throw new InvalidJarPluginException("[FATAL/INTERNAL] " +
|
196
|
+
attributeName +
|
197
|
+
" is considered invalid as a manifest attribute.",
|
198
|
+
ex);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
private final Manifest pluginManifest;
|
203
|
+
private final Attributes pluginManifestAttributes;
|
204
|
+
private final Class pluginMainClass;
|
205
|
+
}
|