[ ] Java- 2017 |
, , Java 9.
: Elasticsearch . , . Elasticsearch, rolling upgrade. - (, - ) reindex . : , . 2 .
Java API , , Elasticsearch. , 5- Rest Client, ( API Elasticsearch), 2- .
: 2- Elasticsearch 1.7 2.4. , ( OSGi).
. Maven- :
+---pom.xml
+---core/
| +---pom.xml
| +---src/
| +---main/
| | +---java/
| | | +---elasticsearch/
| | | +---client/
| | | +---SearchClient.java
| | | +---Searcher.java
| | +---resources/
| +---test/
| +---java/
+---es-v1/
| +---pom.xml
| +---src/
| +---main/
| | +---java/
| | | +---elasticsearch/
| | | +---client/
| | | +---v1/
| | | +---SearchClientImpl.java
| | +---resources/
| +---test/
| +---java/
+---es-v2/
+---pom.xml
+---src/
+---main/
| +---java/
| | +---elasticsearch/
| | +---client/
| | +---v2/
| | +---SearchClientImpl.java
| +---resources/
+---test/
+---java/
, , :
core Searcher
, "" es-v1 es-v2:
public class Searcher {
public static void main(String[] args) throws Exception {
List clients = Arrays.asList(
getClient("1"),
getClient("2")
);
for (SearchClient client : clients) {
System.out.printf("Client for version: %s%n", client.getVersion());
Map doc = client.search("test");
System.out.println("Found doc:");
System.out.println(doc);
System.out.println();
}
clients.forEach(SearchClient::close);
}
private static SearchClient getClient(String desiredVersion) throws Exception {
return null; // .
}
}
: Elasticsearch, , .
, :
public class SearchClientImpl implements SearchClient {
private final Settings settings = ImmutableSettings.builder()
.put("cluster.name", "es1")
.put("node.name", "es1")
.build();
private final Client searchClient = new TransportClient(settings)
.addTransportAddress(getAddress());
private InetSocketTransportAddress getAddress() {
return new InetSocketTransportAddress("127.0.0.1", 9301);
}
@Override
public String getVersion() {
return Version.CURRENT.number();
}
@Override
public Map search(String term) {
SearchResponse response = searchClient.prepareSearch("*")
.setQuery(QueryBuilders.termQuery("field", term))
.execute()
.actionGet();
if (response.getHits().getTotalHits() > 0) {
return response.getHits().getAt(0).getSource();
} else {
return null;
}
}
@Override
public void close() {
searchClient.close();
}
}
: , Elasticsearch, field
(*
), , .
, SearchClient
Searcher#getClient
.
Java, , ClassLoader. , , :
private static SearchClient getClient(String desiredVersion) throws Exception {
String className = String.format("elasticsearch.client.v%s.SearchClientImpl", desiredVersion);
return (SearchClient) Class.forName(className).newInstance();
}
, , , :
Exception in thread "main" java.lang.IncompatibleClassChangeError: Implementing class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at elasticsearch.client.Searcher.getClient(Searcher.java:28)
at elasticsearch.client.Searcher.main(Searcher.java:10)
ClassNotFoundException
...
URLClassLoader
jar- , . - , class-path elasticsearch-2.4.5.jar
elasticsearch-1.7.5.jar
, ( ) 2.4.5. Searcher Elasticsearch 1.7.5 (getClient("1")
), URLClassLoader ...
( ) , jar hell ( class-path hell).
, . URLClassLoader es-v* jar-:
private static SearchClient getClient(String desiredVersion) throws Exception {
String className = String.format("elasticsearch.client.v%s.SearchClientImpl", desiredVersion);
Path moduleDependencies = Paths.get("modules", "es-v" + desiredVersion);
URL[] jars = Files.list(moduleDependencies)
.map(Path::toUri)
.map(Searcher::toURL)
.toArray(URL[]::new);
ClassLoader classLoader = new URLClassLoader(jars); // parent = app's class loader
return (SearchClient) classLoader.loadClass(className).newInstance();
}
modules/es-v*/
, maven-dependency-plugin
es-v1 es-v2.
:
mvn package
:
. 29, 2017 10:37:08 org.elasticsearch.plugins.PluginsService
INFO: [es1] loaded [], sites []
. 29, 2017 10:37:12 org.elasticsearch.plugins.PluginsService
INFO: [es2] modules [], plugins [], sites []
Client for version: 1.7.5
Found doc:
{field=test 1}
Client for version: 2.4.5
Found doc:
{field=test 2}
!
JvmInfo, Elasticsearch 1.7.
- Invoke findLoadedClass(String) to check if the class has already been loaded.
- Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
- Invoke the findClass(String) method to find the class.
Elasticsearch core, es-v*. , : , , 2 3. es-v*, core.
URLClassLoader, , , ParentLastURLClassLoader:
public class ParentLastURLClassLoader extends URLClassLoader {
...
}
loadClass(String,boolean)
, ClassLoader :
@Override
protected Class c = findLoadedClass(name);
if (c == null) {
try {
if (getParent() != null) {
c = getParent().loadClass(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
getParent().loadClass(String)
findClass(String)
:
@Override
protected Class c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException ignored) {
}
if (c == null) {
c = getParent().loadClass(name);
if(c == null) {
throw new ClassNotFoundException(name);
}
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
, ClassNotFoundException
, .
, , URLClassLoader
getClient(String)
:
ClassLoader classLoader = new URLClassLoader(jars);
ParentLastURLClassLoader
:
ClassLoader classLoader = new ParentLastClassLoader(jars);
, :
. 29, 2017 10:42:41 org.elasticsearch.plugins.PluginsService
INFO: [es1] loaded [], sites []
. 29, 2017 10:42:44 org.elasticsearch.plugins.PluginsService
INFO: [es2] modules [], plugins [], sites []
Client for version: 1.7.5
Found doc:
{field=test 1}
Client for version: 2.4.5
Found doc:
{field=test 2}
Java 6 java.util.ServiceLoader, / . :
private static SearchClient getClient(String desiredVersion) throws Exception {
Path moduleDependencies = Paths.get("modules", "es-v" + desiredVersion);
URL[] jars = Files.list(moduleDependencies)
.map(Path::toUri)
.map(Searcher::toURL)
.toArray(URL[]::new);
ServiceLoader serviceLoader = ServiceLoader.load(SearchClient.class, new URLClassLoader(jars));
return serviceLoader.iterator().next();
}
:
ServiceLoader , META-INF/services
:
+---es-v1/
| +---src/
| +---main/
| +---resources/
| +---META-INF/
| +---services/
| +---elasticsearch.client.spi.SearchClient
+---es-v2/
+---src/
+---main/
+---resources/
+---META-INF/
+---services/
+---elasticsearch.client.spi.SearchClient
, , : SearchClientImpl
.
es-v1 :
elasticsearch.client.v1.SearchClientImpl
es-v2:
elasticsearch.client.v2.SearchClientImpl
maven-dependency-plugin
es-v* modules/es-v*/
. :
mvn clean package
:
. 29, 2017 10:50:17 org.elasticsearch.plugins.PluginsService
INFO: [es1] loaded [], sites []
. 29, 2017 10:50:20 org.elasticsearch.plugins.PluginsService
INFO: [es2] modules [], plugins [], sites []
Client for version: 1.7.5
Found doc:
{field=test 1}
Client for version: 2.4.5
Found doc:
{field=test 2}
, .
SearchClient
spi :
core: bootstrap -> system
spi: bootstrap -> spi
es-v1: bootstrap -> spi -> es-v1
es-v2: bootstrap -> spi -> es-v2
..
SearchClient
),OSGi ( ?). Apache Felix Eclipse Equinox, , (?) . , . , ( , - ).
, .. Java 9 , .
9- , . , !
Hint: , , JDK 9, .
( , IntelliJ IDEA class-path module-path, module-path).
, , ( , ).
, , . boot
, --module-path
, , ( java.base
). .
( ). , . , .
. ( module-info.java
) , . , , , . ServiceLoader API .
( , ):
module-info.class
jar- ( jar),class-path
., :
elasticsearch.shaded
.Searcher
es-v* , ServiceLoader API.?...
( ).
, , - API Functions
. Helpers
. :
com.foo.bar
public class Functions
class Helpers
, :
com.foo.baz
public class Additional
Helpers
. , - :
com.foo.baz
public class Additional
com.foo.bar
public class AccessorToHelpers
(split package) . ? class-path , . , : jar- ( fat jar uber jar), - module-path , class-path. all-in-one jar.
Elasticsearch package-private / Lucene. , uber jar elasticsaerch-shaded
.
Maven-, : pom.xml
, 8-.
- , v1.7.5
:
maven-shade-plugin
, uberjar , :
, :
org.codehaus.groovy:groovy-all
org.slf4j:*
log4j:*
:
pom.xml
, UnsupportedOperationException
, java.lang.management.RuntimeMXBean#getBootClassPath
. JvmInfo
:info.bootClassPath = runtimeMXBean.getBootClassPath();
"":
if (runtimeMXBean.isBootClassPathSupported()) {
info.bootClassPath = runtimeMXBean.getBootClassPath();
} else {
info.bootClassPath = "";
}
.
, jar:
$ mvn package
elasticsearch-1.7.5.jar
target
. , , elasticsearch-shaded
:
$ mvn install:install-file \
> -Dfile=elasticsearch-1.7.5.jar \
> -DgroupId=org.elasticsearch \
> -DartifactId=elasticsearch-shaded \
> -Dversion=1.7.5 \
> -Dpackaging=jar \
> -DgeneratePom=true
Maven- es-v1:
org.elasticsearch
elasticsearch-shaded
1.7.5
...
v2.4.5
. 2- . , elasticsearch-2.4.5.jar
core.
$ mvn versions:set -DnewVersion=2.4.5
Shading and package relocation removed
Elasticsearch used to shade its dependencies and to relocate packages. We no longer use shading or relocation.
You might need to change your imports to the original package names:
com.google.common
wasorg.elasticsearch.common
com.carrotsearch.hppc
wasorg.elasticsearch.common.hppc
jsr166e
wasorg.elasticsearch.common.util.concurrent.jsr166e
...
shade- core, - :
org.apache.maven.plugins
maven-shade-plugin
package
shade
org.slf4j:*
log4j:*
com.twitter:jsr166e
( sun.misc.Unsafe
, 9- "") core:
com.twitter.jsr166e
java.util.concurrent.atomic
.
animal-sniffer-maven-plugin
( 7- jsr166e), :
, , es-v1, :
$ mvn clean package -pl org.elasticsearch:parent,org.elasticsearch:elasticsearch -DskipTests=true
, , Java, Maven- Java-. (src/main/java
) module-info.java
.
core , , , :
// - elasticsearch.client.core
module elasticsearch.client.core {
// , ,
//
exports elasticsearch.client.spi;
// , SearchClient
// ServiceLoader'
uses elasticsearch.client.spi.SearchClient;
}
es-v1 es-v2 :
elasticsearch.client.core
, .. ,SearchClient
.es-v1:
// - elasticsearch.client.v1
module elasticsearch.client.v1 {
// core SearchClient
requires elasticsearch.client.core;
//
requires elasticsearch.shaded;
// core,
provides elasticsearch.client.spi.SearchClient with elasticsearch.client.v1.SearchClientImpl;
}
es-v2 , v2
.
? ModuleLayer, . , es-v* modules/es-v*/
, :
private static SearchClient getClient(String desiredVersion) throws Exception {
Path modPath = Paths.get("modules", "es-v" + desiredVersion);
ModuleFinder moduleFinder = ModuleFinder.of(modPath);
ModuleLayer parent = ModuleLayer.boot();
Configuration config = parent.configuration().resolve(moduleFinder, ModuleFinder.of(), Set.of("elasticsearch.client.v" + desiredVersion));
ModuleLayer moduleLayer = parent.defineModulesWithOneLoader(config, Thread.currentThread().getContextClassLoader());
ServiceLoader serviceLoader = ServiceLoader.load(moduleLayer, SearchClient.class);
Optional searchClient = serviceLoader.findFirst();
if (searchClient.isPresent()) {
return searchClient.get();
}
throw new Exception("Module 'elasticsearch.client.v" + desiredVersion + "' not found on " + modPath);
}
ModuleLayer#defineModulesWithManyLoaders , es-v* .
, . maven-compiler-plugin
, 3.7.0:
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
Java 9 :
1.9
1.9
maven-dependency-plugin
, :
$ mvn clean package
, :
. 29, 2017 10:59:01 org.elasticsearch.plugins.PluginsService
INFO: [es1] loaded [], sites []
. 29, 2017 10:59:04 org.elasticsearch.plugins.PluginsService
INFO: [es2] modules [], plugins [], sites []
Client for version: 1.7.5
Found doc:
{field=test 1}
Client for version: 2.4.5
Found doc:
{field=test 2}
, , , Java 8 .
, /. :
P.S. Java 9!