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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/build.gradle +16 -1
  3. data/embulk-cli/src/main/java/org/embulk/cli/EmbulkMigrate.java +6 -3
  4. data/embulk-core/build.gradle +7 -0
  5. data/embulk-core/src/main/java/org/embulk/EmbulkService.java +2 -0
  6. data/embulk-core/src/main/java/org/embulk/plugin/InjectedPluginSource.java +1 -1
  7. data/embulk-core/src/main/java/org/embulk/plugin/MavenPluginType.java +21 -6
  8. data/embulk-core/src/main/java/org/embulk/plugin/PluginClassLoader.java +42 -2
  9. data/embulk-core/src/main/java/org/embulk/plugin/PluginSourceNotMatchException.java +6 -0
  10. data/embulk-core/src/main/java/org/embulk/plugin/PluginType.java +2 -1
  11. data/embulk-core/src/main/java/org/embulk/plugin/jar/InvalidJarPluginException.java +14 -0
  12. data/embulk-core/src/main/java/org/embulk/plugin/jar/JarPluginLoader.java +205 -0
  13. data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenArtifactFinder.java +134 -0
  14. data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenArtifactNotFoundException.java +20 -0
  15. data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenPluginSource.java +187 -0
  16. data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenPluginSourceModule.java +22 -0
  17. data/embulk-core/src/main/java/org/embulk/plugin/maven/MavenRepositoryNotFoundException.java +31 -0
  18. data/embulk-core/src/main/resources/embulk/parent_first_packages.properties +3 -1
  19. data/embulk-core/src/main/resources/embulk/parent_first_resources.properties +2 -1
  20. data/embulk-core/src/test/java/org/embulk/EmbulkTestRuntime.java +8 -0
  21. data/embulk-core/src/test/java/org/embulk/plugin/TestPluginType.java +24 -0
  22. data/embulk-core/src/test/java/org/embulk/plugin/TestPluginTypeSerDe.java +17 -0
  23. data/embulk-core/src/test/java/org/embulk/plugin/jar/ExampleJarSpiV0.java +9 -0
  24. data/embulk-core/src/test/java/org/embulk/plugin/jar/JarBuilder.java +101 -0
  25. data/embulk-core/src/test/java/org/embulk/plugin/jar/TestJarPluginLoader.java +60 -0
  26. data/embulk-core/src/test/java/org/embulk/plugin/maven/TestMavenArtifactFinder.java +41 -0
  27. data/embulk-core/src/test/resources/m2.test/.gitignore +1 -0
  28. 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
  29. 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
  30. 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
  31. 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
  32. data/embulk-docs/src/built-in.rst +26 -9
  33. data/embulk-docs/src/release.rst +1 -0
  34. data/embulk-docs/src/release/release-0.8.26.rst +16 -0
  35. data/embulk-standards/src/main/java/org/embulk/standards/ConfigInputPlugin.java +170 -0
  36. data/embulk-standards/src/main/java/org/embulk/standards/StandardPluginModule.java +1 -0
  37. data/lib/embulk/version.rb +1 -1
  38. metadata +58 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a2368360f1531d901df3fb7f5d0fe6069ad3362c
4
- data.tar.gz: ef5f5347e9cfabb4ea31d9e29fd69695c9d4ebe4
3
+ metadata.gz: 67e9c5003f422b52f56fd7347f77dda2ea07d86e
4
+ data.tar.gz: ac13f8e09bcc9998ba716fc96e6e98e0546605ef
5
5
  SHA512:
6
- metadata.gz: af58995886e862f1cc30dcbe23813da71f72bd443eb4b9af62584ff5c4aa49ab85a6c53d717e3258f331020e28f4e7b2ff9701c1fde24f1e58d93bff89705387
7
- data.tar.gz: 791c332fcac833460e2a5458d72d8f937ec5881af769fe4100f1b30e225e7b6ed4db0c058f0eec5274f8d3cef56f86a73ff79e95d1987f5f479ad56330f77135
6
+ metadata.gz: 6a275c7189877da26bed04a116dab321b0bf5d9c8f378143aa743c2fa05ae0806fb8d27eabaad76c6bdd7b06971a1b8f5ea2aa8a52152aa7e67a129545e417bc
7
+ data.tar.gz: 6ce56d2cfec57fc2db846f7be7aaf5209e30ac5180b28f0d9930f7cebd333f614e443dcb1e57b49bc58683e4b1a595239809bbe50d4915f9bd21e5d59597abb7
@@ -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.25'
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 modifieid files.");
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(
@@ -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
 
@@ -37,7 +37,7 @@ public class InjectedPluginSource
37
37
  this.injector = injector;
38
38
  }
39
39
 
40
- public static interface PluginFactory <T>
40
+ private static interface PluginFactory <T>
41
41
  {
42
42
  public T newPlugin(Injector injector);
43
43
  }
@@ -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(final String name, final String group, final String version)
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 not present.");
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
- Class<?> loadedClass = findLoadedClass(name);
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
+ }