Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.landawn.abacus.remote.RemoteExecutor Maven / Gradle / Ivy
/*
* Copyright (C) 2015 HaiYang Li
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.landawn.abacus.remote;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import com.landawn.abacus.http.ContentFormat;
import com.landawn.abacus.http.HttpClient;
import com.landawn.abacus.http.HttpSettings;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.parser.KryoParser;
import com.landawn.abacus.parser.ParserFactory;
import com.landawn.abacus.pool.KeyedObjectPool;
import com.landawn.abacus.pool.PoolFactory;
import com.landawn.abacus.pool.PoolableWrapper;
import com.landawn.abacus.util.AddrUtil;
import com.landawn.abacus.util.AsyncExecutor;
import com.landawn.abacus.util.ContinuableFuture;
import com.landawn.abacus.util.DependencyFinder;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.Strings;
import com.landawn.abacus.util.stream.Stream;
// TODO: Auto-generated Javadoc
/**
* Execute the code/method on remote severs, without deploying changes to the target servers first.
* It's required to deploy com.landawn.abacus.da.http.JavaExecutionServlet
under Tomcat or other servlet containers on target servers first.
* Here is the sample for web.xml to deploy the servlet under Tomcat:
*
*
* {@code
*
* Hello javaExecution
* javaExecution
* javaExecution
* com.landawn.abacus.remote.JavaExecutionServlet
*
*
*
* javaExecution
* /javaExecution/*
*
* }
*
* Anonymous classes are supported except the ones created by lambda. For example:
*
* RemoteTask, Object> remoteTask = new RemoteTask() {
* public Object run(Object t) {
* // doing something.
* return null;
* }
* };
*
* remoteExecutor.execute(remoteTask, param);
*
*
* @author Haiyang Li
* @since 0.8
*/
public final class RemoteExecutor {
private static final Logger logger = LoggerFactory.getLogger(RemoteExecutor.class);
private static final Set filteredClassNameSet = N.asSet();
static {
filteredClassNameSet.add("java.*");
filteredClassNameSet.add("javax.*");
filteredClassNameSet.add("com.landawn.abacus.*");
filteredClassNameSet.add("org.apache.*");
filteredClassNameSet.add("org.eclipse.*");
filteredClassNameSet.add("org.junit.*");
filteredClassNameSet.add("org.hamcrest.*");
filteredClassNameSet.add("org.mockito.*");
filteredClassNameSet.add("org.xerial.snappy.*");
filteredClassNameSet.add("net.jpountz.lz4.*");
filteredClassNameSet.add("org.joda.*");
filteredClassNameSet.add("org.springframework.*");
filteredClassNameSet.add("org.hibernate.*");
filteredClassNameSet.add("org.elasticsearch.*");
filteredClassNameSet.add("com.mysql.*");
filteredClassNameSet.add("org.postgresql.*");
filteredClassNameSet.add("oracle.jdbc.*");
filteredClassNameSet.add("com.microsoft.sqlserver.*");
filteredClassNameSet.add("com.ibm.db2.*");
filteredClassNameSet.add("org.hsqldb.*");
filteredClassNameSet.add("org.h2.*");
filteredClassNameSet.add("com.mongodb.*");
filteredClassNameSet.add("com.datastax.driver.*");
filteredClassNameSet.add("com.couchbase.*");
filteredClassNameSet.add("io.netty.*");
filteredClassNameSet.add("com.fasterxml.*");
filteredClassNameSet.add("net.spy.memcached.*");
filteredClassNameSet.add("redis.clients.*");
filteredClassNameSet.add("com.esotericsoftware.kryo.*");
filteredClassNameSet.add("ch.qos.logback.*");
filteredClassNameSet.add("org.slf4j.*");
filteredClassNameSet.add("org.json.*");
filteredClassNameSet.add("org.easymock.*");
filteredClassNameSet.add("org.codehaus.*");
filteredClassNameSet.add("org.aspectj.*");
filteredClassNameSet.add("org.javassist.*");
filteredClassNameSet.add("com.amazonaws.*");
filteredClassNameSet.add("com.alibaba.fastjson.*");
filteredClassNameSet.add("com.google.common.*"); // for Guava
filteredClassNameSet.add("com.google.gson.*");
filteredClassNameSet.add("com.google.appengine.*");
filteredClassNameSet.add("com.google.inject.guice.*");
filteredClassNameSet.add("com.google.protobuf.*");
}
private static final Map, LinkedHashMap> classBytesMapPool = new ConcurrentHashMap<>();
/**
* To set the global properties.
*/
static final KeyedObjectPool> pool = PoolFactory.createKeyedObjectPool(8192);
static final KryoParser kryoParser = ParserFactory.isKryoAvailable() ? ParserFactory.createKryoParser() : null;
static final String hostName = IOUtil.getHostName();
/**
* Get global property with the specified key.
*
* @param
* @param key
* @return
*/
public static T getProperty(Object key) {
final PoolableWrapper wrapper = pool.get(key);
return wrapper == null ? null : (T) wrapper.value();
}
/**
* Get global property with the specified key.
* @param key
* @param targetType
*
* @param
* @return
*/
public static T getProperty(Object key, Class targetType) {
final PoolableWrapper wrapper = pool.get(key);
return N.convert(wrapper == null ? null : wrapper.value(), targetType);
}
/**
* Set global property with the specified key and value.
*
* @param key
* @param value
*/
public static void setProperty(Object key, Object value) {
pool.put(key, PoolableWrapper.of(value));
}
/**
* Set global property with the specified key and value.
*
* @param key
* @param value
* @param liveTime
* @param maxIdleTime
*/
public static void setProperty(Object key, Object value, long liveTime, long maxIdleTime) {
pool.put(key, PoolableWrapper.of(value, liveTime, maxIdleTime));
}
/**
* remove global property with the specified key and value.
*
* @param key
* @return
*/
public static Object removeProperty(Object key) {
return pool.remove(key);
}
private final List httpClients;
private final AsyncExecutor asyncExecutor;
private final Predicate super String> excludeClassByName;
public RemoteExecutor(String servers) {
this(servers, null);
}
public RemoteExecutor(String servers, Predicate super String> classNamefilter) {
this(AddrUtil.getServerList(servers), classNamefilter);
}
public RemoteExecutor(Collection servers) {
this(servers, null);
}
/**
*
* @param servers
* @param excludeClassByName exclude classes from the dependency and don't upload them to remote server.
*/
public RemoteExecutor(final Collection servers, final Predicate super String> excludeClassByName) {
httpClients = new ArrayList<>(servers.size());
for (String server : servers) {
httpClients.add(HttpClient.create(server, HttpClient.DEFAULT_MAX_CONNECTION, Integer.MAX_VALUE, Integer.MAX_VALUE));
}
asyncExecutor = new AsyncExecutor(Math.min(8, servers.size()), servers.size(), 180L, TimeUnit.SECONDS);
this.excludeClassByName = new Predicate<>() {
@Override
public boolean test(String value) {
if (excludeClassByName != null && excludeClassByName.test(value)) {
return true;
}
for (String clsName : filteredClassNameSet) {
if (value.matches(clsName)) {
return true;
}
}
return false;
}
};
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @return
*/
public List execute(final Class extends RemoteTask, T>> remoteTask) {
return execute(remoteTask, (Object) null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List execute(final Class extends RemoteTask, T>> remoteTask, final Object parameter) {
return execute(remoteTask, parameter, null, Long.MAX_VALUE, null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @param totalExecutionTimeout
* @param serverFilter filter the servers by the input parameter or other logic. There are two elements in the array. left is the target server url, right is the input parameter.
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List execute(final Class extends RemoteTask, T>> remoteTask, final Object parameter,
final HttpSettings httpSettings, final long totalExecutionTimeout, final BiPredicate serverFilter) {
return execute(remoteTask, parameter, null, httpSettings, totalExecutionTimeout, serverFilter);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List execute(final Class extends RemoteTask, T>> remoteTask, final Map serverParameterMapper) {
return execute(remoteTask, serverParameterMapper, null, Long.MAX_VALUE);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @param totalExecutionTimeout
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List execute(final Class extends RemoteTask, T>> remoteTask, final Map serverParameterMapper,
final HttpSettings httpSettings, final long totalExecutionTimeout) {
return execute(remoteTask, null, serverParameterMapper, httpSettings, totalExecutionTimeout, null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask must be equals to the instance obtained by remoteTask.getClass().newInstance(). That's to say no extra properties has been set after the object was created.
* @return
*/
public List execute(final RemoteTask, T> remoteTask) {
return execute(remoteTask, (Object) null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask must be equals to the instance obtained by remoteTask.getClass().newInstance(). That's to say no extra properties has been set after the object was created.
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List execute(final RemoteTask, T> remoteTask, final Object parameter) {
return execute(remoteTask, parameter, null, Long.MAX_VALUE, null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask must be equals to the instance obtained by remoteTask.getClass().newInstance(). That's to say no extra properties has been set after the object was created.
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @param totalExecutionTimeout
* @param serverFilter filter the servers by the input parameter or other logic. There are two elements in the array. left is the target server url, right is the input parameter.
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List execute(final RemoteTask, T> remoteTask, final Object parameter, final HttpSettings httpSettings,
final long totalExecutionTimeout, final BiPredicate serverFilter) {
return execute((Class extends RemoteTask, T>>) remoteTask.getClass(), parameter, httpSettings, totalExecutionTimeout, serverFilter);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask must be equals to the instance obtained by remoteTask.getClass().newInstance(). That's to say no extra properties has been set after the object was created.
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List execute(final RemoteTask, T> remoteTask, final Map serverParameterMapper) {
return execute(remoteTask, serverParameterMapper, null, Long.MAX_VALUE);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask must be equals to the instance obtained by remoteTask.getClass().newInstance(). That's to say no extra properties has been set after the object was created.
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @param totalExecutionTimeout
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List execute(final RemoteTask, T> remoteTask, final Map serverParameterMapper,
final HttpSettings httpSettings, final long totalExecutionTimeout) {
return execute((Class extends RemoteTask, T>>) remoteTask.getClass(), serverParameterMapper, httpSettings, totalExecutionTimeout);
}
/**
*
* @param
* @param remoteTask
* @param parameter
* @param serverParameterMapper
* @param httpSettings
* @param totalExecutionTimeout
* @param serverFilter
* @return
*/
private List execute(final Class extends RemoteTask, T>> remoteTask, final Object parameter,
final Map serverParameterMapper, final HttpSettings httpSettings, final long totalExecutionTimeout,
final BiPredicate serverFilter) {
final long startTime = System.currentTimeMillis();
final List> futureResponses = asyncExecute(remoteTask, parameter, serverParameterMapper, httpSettings,
serverFilter);
final List result = new ArrayList<>(futureResponses.size());
RemoteExecutionResponse response = null;
for (Future futureResponse : futureResponses) {
final long remainingExecutionTime = totalExecutionTimeout - (System.currentTimeMillis() - startTime);
try {
response = futureResponse.get(remainingExecutionTime > 0 ? remainingExecutionTime : 1, TimeUnit.MILLISECONDS);
} catch (Exception e) {
response = new RemoteExecutionResponse();
response.setErrorCode(getClassName(e.getClass()));
response.setErrorMessage(e.getMessage());
response.setRequestHost(hostName);
response.setRequestTime(startTime);
response.setElapsedTime(System.currentTimeMillis() - startTime);
}
result.add(response);
}
return result;
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @return
*/
public List> asyncExecute(final Class extends RemoteTask, T>> remoteTask) {
return asyncExecute(remoteTask, (Object) null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List> asyncExecute(final Class extends RemoteTask, T>> remoteTask, final Object parameter) {
return asyncExecute(remoteTask, parameter, null, null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @param serverFilter filter the servers by the input parameter or other logic. There are two elements in the array. left is the target server url, right is the input parameter.
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List> asyncExecute(final Class extends RemoteTask, T>> remoteTask, final Object parameter,
final HttpSettings httpSettings, final BiPredicate serverFilter) {
return asyncExecute(remoteTask, parameter, null, httpSettings, serverFilter);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List> asyncExecute(final Class extends RemoteTask, T>> remoteTask,
final Map serverParameterMapper) {
return asyncExecute(remoteTask, serverParameterMapper, null);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List> asyncExecute(final Class extends RemoteTask, T>> remoteTask,
final Map serverParameterMapper, final HttpSettings httpSettings) {
return asyncExecute(remoteTask, null, serverParameterMapper, httpSettings, null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @return
*/
public List> asyncExecute(final RemoteTask, T> remoteTask) {
return asyncExecute(remoteTask, (Object) null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List> asyncExecute(final RemoteTask, T> remoteTask, final Object parameter) {
return asyncExecute(remoteTask, parameter, null, null);
}
/**
* Execute the task on different remote servers in parallel with parameter
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param parameter it must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @param serverFilter filter the servers by the input parameter or other logic. There are two elements in the array. left is the target server url, right is the input parameter.
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List> asyncExecute(final RemoteTask, T> remoteTask, final Object parameter,
final HttpSettings httpSettings, final BiPredicate serverFilter) {
return asyncExecute((Class extends RemoteTask, T>>) remoteTask.getClass(), parameter, httpSettings, serverFilter);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @return
*/
public List> asyncExecute(final RemoteTask, T> remoteTask, final Map serverParameterMapper) {
return asyncExecute(remoteTask, serverParameterMapper, null);
}
/**
* Execute the task on different remote servers in parallel with different element in parameters
.
* The types of parameter/return value only can be the classes exists in JDK or common libraries deployed on target servers.
* It's one of the best practices to transfer big request parameter or response result by DataSet
*
* @param
* @param remoteTask which has and only has the default constructor
* @param serverParameterMapper the key is the target host/url and the value must be primitive types/string (array)/date/collection/map/bean... which can be serialized to JSON.
* The element in the Collection or key/value in Map must be concrete types: primitive types wrapper/String/Date/Bean..., can't be Collection or Map again.
* @param httpSettings refer to com.landawn.abacus.http.AbstractHttpClient.HttpSettings
* @return
* @see com.landawn.abacus.http.HttpSettings
*/
public List> asyncExecute(final RemoteTask, T> remoteTask, final Map serverParameterMapper,
final HttpSettings httpSettings) {
return asyncExecute((Class extends RemoteTask, T>>) remoteTask.getClass(), serverParameterMapper, httpSettings);
}
/**
*
* @param
* @param remoteTask
* @param parameter
* @param serverParameterMapper
* @param httpSettings
* @param serverFilter
* @return
*/
@SuppressWarnings({ "deprecation" })
private List> asyncExecute(final Class extends RemoteTask, T>> remoteTask, final Object parameter,
final Map serverParameterMapper, final HttpSettings httpSettings, final BiPredicate serverFilter) {
if (kryoParser == null) {
throw new RuntimeException("Kryo libraries are required");
}
final HttpSettings newHttpSettings = httpSettings == null ? HttpSettings.create() : httpSettings.copy();
if (newHttpSettings.getContentFormat() == null || newHttpSettings.getContentFormat() == ContentFormat.NONE) {
newHttpSettings.setContentFormat(ContentFormat.JSON);
}
if (!(newHttpSettings.getContentFormat() == ContentFormat.JSON || newHttpSettings.getContentFormat() == ContentFormat.KRYO)) {
throw new IllegalArgumentException("Only format JSON/Kryo is supported");
}
final RemoteExecutionRequest request0 = createRemoteRequest(remoteTask);
if (serverParameterMapper == null) {
request0.setParameter(parameter);
}
if (logger.isInfoEnabled()) {
final long totalClassSizeInByte = Stream.of(request0.getClassBytesMap()).mapToLong(it -> it.getValue().length).sum();
final String msg = Stream.of(request0.getClassBytesMap())
.map(it -> it.getKey() + ": " + it.getValue().length)
.join(Strings.ELEMENT_SEPARATOR, "Classes to be transferred to target servers for execution: ", ". Total size: " + totalClassSizeInByte);
logger.info(msg);
}
final List> futureResponses = new ArrayList<>(httpClients.size());
String paramKey = null;
for (int i = 0, len = httpClients.size(); i < len; i++) {
final HttpClient httpClient = httpClients.get(i);
if (serverParameterMapper == null) {
if (serverFilter != null && serverFilter.test(httpClient.url(), parameter) == false) {
continue;
}
} else {
for (String key : serverParameterMapper.keySet()) {
if (httpClient.url().equals(key) || httpClient.url().contains(key) || Strings.containsIgnoreCase(httpClient.url(), key)) {
paramKey = key;
break;
}
}
if (paramKey == null) {
continue;
}
}
final RemoteExecutionRequest request = paramKey == null ? request0 : N.copy(request0);
request.setParameter(serverParameterMapper == null ? null : serverParameterMapper.get(paramKey));
final Callable cmd = new Callable<>() {
@Override
public RemoteExecutionResponse call() throws Exception {
final RemoteExecutionResponse result = httpClient.post(RemoteExecutionResponse.class, request, newHttpSettings);
result.setRequestHost(request.getRequestHost());
result.setRequestId(request.getRequestId());
result.setRequestTime(request.getRequestTime());
result.setResponseTime(System.currentTimeMillis());
result.setElapsedTime(result.getResponseTime() - result.getRequestTime());
return result;
}
};
if (logger.isInfoEnabled()) {
logger.info("Starting task on remote server: " + httpClient.url());
}
futureResponses.add(asyncExecutor.execute(cmd));
}
return futureResponses;
}
/**
* Creates the remote request.
*
* @param remoteTask
* @return
*/
@SuppressWarnings("deprecation")
private RemoteExecutionRequest createRemoteRequest(final Class extends RemoteTask, ?>> remoteTask) {
final RemoteExecutionRequest request = new RemoteExecutionRequest();
request.setRequestHost(hostName);
request.setRequestId(Strings.uuid());
request.setRequestTime(System.currentTimeMillis());
request.setClassName(getClassName(remoteTask));
request.setClassBytesMap(getClassDependency(remoteTask));
return request;
}
// @SuppressWarnings("deprecation")
// private RemoteExecutionRequest setRequestParameter(final RemoteExecutionRequest request, Object parameter) {
// if (parameter == null) {
// request.setParameterType(null);
// request.setParameterString(null);
// } else {
// final Type paramType = getType(parameter);
//
// request.setParameterType(paramType.getName());
// request.setParameterString(paramType.stringOf(parameter));
// }
//
// return request;
// }
/**
* Gets the class dependency.
*
* @param classToCheck
* @return
*/
private Map getClassDependency(final Class> classToCheck) {
LinkedHashMap classBytesMap = classBytesMapPool.get(classToCheck);
if (classBytesMap == null) {
final List> dependencies = new ArrayList<>(DependencyFinder.getDependenciesRecursively(classToCheck, excludeClassByName));
N.reverse(dependencies);
classBytesMap = new LinkedHashMap<>(dependencies.size() + 1);
for (Class> cls : dependencies) {
classBytesMap.put(getClassName(cls), readClass(cls));
}
final String clsName = getClassName(classToCheck);
if (excludeClassByName.test(clsName) == false) {
classBytesMap.put(getClassName(classToCheck), readClass(classToCheck));
}
classBytesMapPool.put(classToCheck, classBytesMap);
}
return classBytesMap;
}
/**
*
* @param cls
* @return
*/
private static byte[] readClass(Class> cls) {
InputStream is = null;
try {
String clsName = getClassName(cls);
int lastIndex = clsName.lastIndexOf('.');
is = cls.getResourceAsStream((lastIndex < 0 ? clsName : clsName.substring(lastIndex + 1)) + ".class");
return IOUtil.readAllBytes(is);
} finally {
IOUtil.close(is);
}
}
/**
* Gets the class name.
*
* @param cls
* @return
*/
private static String getClassName(final Class> cls) {
return cls.getName();
}
}