-
Notifications
You must be signed in to change notification settings - Fork 261
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8289 from mandy-chessell/oak2024
Add custom class loader to OCF (#8288)
- Loading branch information
Showing
5 changed files
with
225 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
...c/main/java/org/odpi/openmetadata/frameworks/connectors/IsolatedConnectorClassLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* SPDX-License-Identifier: Apache-2.0 */ | ||
/* Copyright Contributors to the ODPi Egeria project. */ | ||
|
||
package org.odpi.openmetadata.frameworks.connectors; | ||
|
||
|
||
import java.io.*; | ||
import java.net.URL; | ||
import java.net.URLClassLoader; | ||
import java.util.jar.JarFile; | ||
|
||
/** | ||
* IsolatedConnectorClassLoader is used by a connector provider to create a connector instance that uses class | ||
* implementations from its own JAR file rather than any class implementations that may have already been loaded. | ||
*/ | ||
public class IsolatedConnectorClassLoader extends URLClassLoader | ||
{ | ||
private final ClassLoader defaultClassLoader; | ||
private final JarFile jarfile; | ||
private final String jarFileName; | ||
private final String jarFileSpec; | ||
|
||
|
||
public IsolatedConnectorClassLoader(String jarFileName) throws IOException | ||
{ | ||
this(jarFileName, | ||
Thread.currentThread().getContextClassLoader().getParent(), | ||
Thread.currentThread().getContextClassLoader()); | ||
} | ||
|
||
|
||
/** | ||
* Creates a new class loader of the specified name and using the | ||
* specified parent class loader for delegation. | ||
* | ||
* @param jarFileName name of the jar file to load from | ||
* @param jdkClassLoader the parent class loader for JRE classes | ||
* @param defaultClassLoader the class loader to use if can not find class in JAR. | ||
*/ | ||
protected IsolatedConnectorClassLoader(String jarFileName, | ||
ClassLoader jdkClassLoader, | ||
ClassLoader defaultClassLoader) throws IOException | ||
{ | ||
super(new URL[]{}, jdkClassLoader); | ||
this.defaultClassLoader = defaultClassLoader; | ||
this. jarFileName = jarFileName; | ||
this.jarfile = new JarFile(jarFileName); | ||
super.addURL(new File(jarFileName).toURI().toURL()); | ||
|
||
this.jarFileSpec = "jar:file:" + jarFileName + "!/"; | ||
super.addURL(new URL(jarFileSpec)); | ||
} | ||
|
||
|
||
/** | ||
* This loads classes from the JDK, then the JAR file, then the default class loader. | ||
* | ||
* @param name The <a href="#binary-name">binary name</a> of the class | ||
* | ||
* @return loaded class | ||
* @throws ClassNotFoundException not found on the class path | ||
*/ | ||
@Override | ||
public Class<?> loadClass(String name) throws ClassNotFoundException | ||
{ | ||
try | ||
{ | ||
return super.findClass(name); | ||
} | ||
catch (ClassNotFoundException notFound) | ||
{ | ||
return defaultClassLoader.loadClass(name); | ||
} | ||
} | ||
} |
133 changes: 133 additions & 0 deletions
133
.../main/java/org/odpi/openmetadata/frameworks/connectors/IsolatedConnectorProviderBase.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* SPDX-License-Identifier: Apache-2.0 */ | ||
/* Copyright Contributors to the ODPi Egeria project. */ | ||
|
||
package org.odpi.openmetadata.frameworks.connectors; | ||
|
||
|
||
import java.io.IOException; | ||
import java.net.MalformedURLException; | ||
import java.net.URI; | ||
import java.net.URISyntaxException; | ||
import java.net.URL; | ||
import java.nio.file.Paths; | ||
|
||
/** | ||
* IsolatedConnectorProviderBase provides extensions to ConnectorProviderBase that uses a custom class loader to | ||
* load the connector class in getConnector. This custom class loader give preference to the classes in the | ||
* same JAR file as the connector provider's implementation over other classes on the class path. | ||
* Note: | ||
* this process assumes that the connector class is not already loaded through a reference in the connector providers | ||
* implementation - ie private static final String connectorClassName should be set to a literal string. | ||
* | ||
*/ | ||
public class IsolatedConnectorProviderBase extends ConnectorProviderBase | ||
{ | ||
|
||
/** | ||
* Use a custom class loader to favour classes that are located in the connector implementation's JAR file. | ||
* | ||
* @return class | ||
* @throws ClassNotFoundException unable to locate a class by that name on the class path | ||
*/ | ||
protected Class<?> getClassForConnector() throws ClassNotFoundException | ||
{ | ||
try | ||
{ | ||
IsolatedConnectorClassLoader isolatedConnectorClassLoader = new IsolatedConnectorClassLoader(this.getJARFileURL(this.getClass())); | ||
|
||
// return Class.forName(getConnectorClassName(), true, isolatedConnectorClassLoader); | ||
|
||
return isolatedConnectorClassLoader.loadClass(getConnectorClassName()); | ||
} | ||
catch (IOException error) | ||
{ | ||
throw new ClassNotFoundException("Bad provider class name", error); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Extract the name of the JAR file from the provider's class. | ||
* | ||
* @param providerClass class for this connector provider | ||
* @return JAR file name in URL form | ||
* @throws MalformedURLException should not happen - this means the class was not loaded from a JAR file. | ||
*/ | ||
private String getJARFileURL(Class<?> providerClass) throws MalformedURLException | ||
{ | ||
URL qualifiedClassURL = providerClass.getResource(providerClass.getSimpleName() + ".class"); | ||
|
||
if (qualifiedClassURL != null) | ||
{ | ||
String qualifiedClassName = qualifiedClassURL.toString(); | ||
String noClassQualifiedName = qualifiedClassName.split("!")[0]; | ||
String[] noJarQualifiedNameSplit = noClassQualifiedName.split("jar:file:"); | ||
|
||
String noJarQualifiedName = noJarQualifiedNameSplit[noJarQualifiedNameSplit.length - 1]; | ||
|
||
return noJarQualifiedName; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
|
||
/** | ||
* Return the path name of the Connector Provider's JAR file. | ||
* | ||
* @return file name of this connector provider's jar file | ||
*/ | ||
private String getMyJARFilePath() | ||
{ | ||
try | ||
{ | ||
return byGetProtectionDomain(this.getClass()); | ||
} | ||
catch (Exception error) | ||
{ | ||
// Cannot get jar file path using byGetProtectionDomain because the runtime does not permit it | ||
} | ||
|
||
return byGetResource(this.getClass()); | ||
} | ||
|
||
|
||
|
||
/** | ||
* This method | ||
* @param providerClass | ||
* @return | ||
* @throws URISyntaxException | ||
*/ | ||
String byGetProtectionDomain(Class<?> providerClass) throws URISyntaxException | ||
{ | ||
URL url = providerClass.getProtectionDomain().getCodeSource().getLocation(); | ||
return Paths.get(url.toURI()).toString(); | ||
} | ||
|
||
String byGetResource(Class<?> providerClass) | ||
{ | ||
final URL classResource = providerClass.getResource(providerClass.getSimpleName() + ".class"); | ||
if (classResource == null) | ||
{ | ||
throw new RuntimeException("class resource is null"); | ||
} | ||
|
||
final String url = classResource.toString(); | ||
if (url.startsWith("jar:file:")) | ||
{ | ||
// extract 'file:......jarName.jar' part from the url string | ||
String path = url.replaceAll("^jar:(file:.*[.]jar)!/.*", "$1"); | ||
try | ||
{ | ||
return Paths.get(new URI(path)).toString(); | ||
} | ||
catch (Exception error) | ||
{ | ||
throw new RuntimeException("Invalid Jar File URL String", error); | ||
} | ||
} | ||
|
||
throw new RuntimeException("Invalid Jar File URL String"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters