Я перевожу приложение J2EE EJB на сервисы Spring.Это настольное приложение, которое имеет графический интерфейс Swing и для связи с сервером J2EE использует RMI.Я создал простой весенний сервис с весенней загрузкой, который экспортирует сервис с использованием удаленного взаимодействия, RMIServiceExporter.Клиент - богатый клиент и имеет сложную архитектуру, поэтому я пытаюсь внести в него минимальные изменения, чтобы вызвать службу Spring RMI.
Итак, в целом, у меня есть простой клиент RMI и сервер RMI Spring.Я узнал, что Spring RMI абстрагирует чистый Java RMI, поэтому в моем случае они не взаимодействуют.
Я покажу код ниже, но текущая ошибка это.Обратите внимание, что мой текущий проект использует «remote: //».Таким образом, после того, как я получил эту ошибку, я также попробовал «rmi: //».Но в обоих случаях это выдает эту ошибку.
javax.naming.CommunicationException: Failed to connect to any server. Servers tried: [rmi://yyy:1099 (No connection provider for URI scheme "rmi" is installed)]
at org.jboss.naming.remote.client.HaRemoteNamingStore.failOverSequence(HaRemoteNamingStore.java:244)
at org.jboss.naming.remote.client.HaRemoteNamingStore.namingStore(HaRemoteNamingStore.java:149)
at org.jboss.naming.remote.client.HaRemoteNamingStore.namingOperation(HaRemoteNamingStore.java:130)
at org.jboss.naming.remote.client.HaRemoteNamingStore.lookup(HaRemoteNamingStore.java:272)
at org.jboss.naming.remote.client.RemoteContext.lookupInternal(RemoteContext.java:104)
at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:93)
at org.jboss.naming.remote.client.RemoteContext.lookup(RemoteContext.java:146)
at javax.naming.InitialContext.lookup(InitialContext.java:417)
at com.xxx.ui.common.communication.JbossRemotingInvocationFactory.getRemoteObject(JbossRemotingInvocationFactory.java:63)
at com.xxx.gui.comm.CommManager.initializeSpringEJBz(CommManager.java:806)
at com.xxx.gui.comm.CommManager.initializeEJBz(CommManager.java:816)
at com.xxx.gui.comm.CommManager.initializeAndLogin(CommManager.java:373)
at com.xxx.gui.comm.CommManager$2.doInBackground(CommManager.java:273)
at javax.swing.SwingWorker$1.call(SwingWorker.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at javax.swing.SwingWorker.run(SwingWorker.java:334)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Я искал, как мы можем взаимодействовать с Spring rmi и plain / pure java rmi, и я прочитал несколько ответов на похожие вопросы в stackoverflow и в Интернете, но я не смогне нашел ничего полезного или подходящего для моего случая, потому что даже самый подходящий ответ говорит только о том, что он не взаимодействует.
Я подумал, что, возможно, мне нужно превратить свой клиент Swi GUI в весенний с помощью весенней загрузки, но я не был уверен в контексте приложения, так как не хочу нарушать существующий клиентский код.Итак, я искал, может быть, есть что-то вроде частичного контекста Spring, так что, возможно, я могу поместить в него только мой клиентский код CommManager.java, а Spring только управляет этим файлом.измените мой RMI-сервер, чтобы заставить Spring создать какой-то простой / чистый Java RMI вместо RMI по умолчанию.Я говорю что-то, потому что я прочитал кое-что о Spring rmi, которое объясняет, что это абстракция над rmi, и мы можем заставить его создать стандартную заглушку RMI.
Пока я ищу решение, я столкнулся с Spring Integration, но яне мог понять это на самом деле, так как это выглядит как другая абстракция, но это также говорит о адаптерах.Так как я видел «адаптер», возможно, он используется для такого рода случаев интеграции / миграции устаревшего кода.Но я не мог идти дальше.
Клиентская сторона:
CommManager.java
private boolean initializeEJBz(String userName, String password) throws Exception {
...
ri = RemoteInvocationFactory.getRemoteInvocation(user, pass);
if (ri != null) {
return initializeEJBz(ri);
} else {
return false;
}
}
RemoteInvocationFactory.java
package com.xxx.ui.common.communication;
import javax.naming.NamingException;
public final class RemoteInvocationFactory {
private static final CommunicationProperties cp = new CommunicationProperties();
public static synchronized RemoteInvocation getRemoteInvocation(
byte[] userName, byte[] password) throws NamingException {
String url = System.getProperty("rmi://xxx.com:1099");
if (url != null) {
return new JbossRemotingInvocationFactory(userName, password, url);
}
return null;
}
...
JbossRemotingInvocationFactory.java
package com.xxx.ui.common.communication;
...
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
...
import java.util.Hashtable;
import java.util.concurrent.TimeUnit;
public class JbossRemotingInvocationFactory implements RemoteInvocation {
private final byte[] userName, password;
private final String providerURL;
private volatile InitialContext initialContext;
private final SecretKey secretKey;
private static final String SSL_ENABLED = "jboss.naming.client.connect.options.org.xnio.Options.SSL_ENABLED";
private static final String SSL_STARTTLS = "jboss.naming.client.connect.options.org.xnio.Options.SSL_STARTTLS";
private static final String TIMEOUT = "jboss.naming.client.connect.timeout";
private long timeoutValue;
private final boolean startSsl;
@SuppressWarnings("unchecked")
public JbossRemotingInvocationFactory(byte[] userName, byte[] password, String providerURL) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
secretKey = keyGenerator.generateKey();
this.providerURL = providerURL;
startSsl = Boolean.valueOf(System.getProperty(SSL_ENABLED));
String property = System.getProperty("myproject.connect.timeout");
if (property != null) {
try {
timeoutValue = TimeUnit.MILLISECONDS.convert(Long.parseLong(property), TimeUnit.SECONDS);
} catch (Exception e) {
timeoutValue = TimeUnit.MILLISECONDS.convert(10, TimeUnit.SECONDS);
}
}
Hashtable jndiProperties = new Hashtable();
this.userName = encrypt(userName);
addOptions(jndiProperties);
jndiProperties.put(Context.SECURITY_CREDENTIALS, new String(password, UTF_8));
initialContext = new InitialContext(jndiProperties);
this.password = encrypt(password);
} catch (NamingException | NoSuchAlgorithmException ne) {
throw new RuntimeException(ne);
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T getRemoteObject(Class<T> object, String jndiName) throws NamingException {
if (initialContext != null) {
T value = (T) initialContext.lookup(jndiName);
initialContext.removeFromEnvironment(Context.SECURITY_CREDENTIALS);
initialContext.removeFromEnvironment(Context.SECURITY_PRINCIPAL);
return value;
} else {
throw new IllegalStateException();
}
}
@Override
public <T> T getRemoteObject(Class<T> object) throws NamingException {
throw new IllegalAccessError();
}
...
private void addOptions(Hashtable jndiProperties) {
jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
jndiProperties.put("jboss.naming.client.ejb.context", "true");
jndiProperties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
jndiProperties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
jndiProperties.put(SSL_STARTTLS, "false");
jndiProperties.put(TIMEOUT, Long.toString(timeoutValue));
if (startSsl) {
jndiProperties.put("jboss.naming.client.remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "true");
jndiProperties.put(SSL_ENABLED, "true");
}
jndiProperties.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS", "JBOSS-LOCAL-USER");
jndiProperties.put(Context.PROVIDER_URL, providerURL);
jndiProperties.put(Context.SECURITY_PRINCIPAL, new String(decrypt(userName), UTF_8));
}
@Override
public void reconnect() {
try {
Hashtable jndiProperties = new Hashtable();
addOptions(jndiProperties);
jndiProperties.put(Context.SECURITY_CREDENTIALS, new String(decrypt(password), UTF_8));
initialContext = new InitialContext(jndiProperties);
} catch (NamingException ignore) {
}
}
}
CommManager.java
private boolean initializeEJBz(RemoteInvocation remoteInvocation) throws Exception {
cs = remoteInvocation.getRemoteObject(CustomerService.class, JNDINames.CUSTOMER_SERVICE_REMOTE);
...
// here is the integration point. try to get RMI service exported.
myService = remoteInvocation.getRemoteObject(HelloWorldRMI.class, JNDINames.HELLO_WORLD_REMOTE);
return true;
}
public static final String CUSTOMER_SERVICE_REMOTE = getRemoteBean("CustomerServiceBean", CustomerService.class.getName());
public static final string HELLO_WORLD_REMOTE = getRemoteBean("HelloWorldRMI", HelloWorldRMI.class.getName());
...
private static final String APPLICATION_NAME = "XXX";
private static final String MODULE_NAME = "YYYY";
...
protected static String getRemoteBean(String beanName, String interfaceName) {
return String.format("%s/%s/%s!%s", APPLICATION_NAME, MODULE_NAME, beanName, interfaceName);
}
Сторона сервера:
HelloWorldRMI.java:
package com.example.springrmiserver.service;
public interface HelloWorldRMI {
public String sayHelloRmi(String msg);
}
HelloWorldRMIImpl:
package com.example.springrmiserver.service;
import java.util.Date;
public class HelloWorldRMIimpl implements HelloWorldRMI {
@Override
public String sayHelloRmi(String msg) {
System.out.println("================Server Side ========================");
System.out.println("Inside Rmi IMPL - Incoming msg : " + msg);
return "Hello " + msg + " :: Response time - > " + new Date();
}
}
Config.java:
package com.example.springrmiserver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.rmi.RmiServiceExporter;
import org.springframework.remoting.support.RemoteExporter;
import com.example.springrmiserver.service.HelloWorldRMI;
import com.example.springrmiserver.service.HelloWorldRMIimpl;
@Configuration
public class Config {
@Bean
RemoteExporter registerRMIExporter() {
RmiServiceExporter exporter = new RmiServiceExporter();
exporter.setServiceName("helloworldrmi");
//exporter.setRegistryPort(1190);
exporter.setServiceInterface(HelloWorldRMI.class);
exporter.setService(new HelloWorldRMIimpl());
return exporter;
}
}
SpringServerApplication.java:
package com.example.springrmiserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Collections;
@SpringBootApplication
public class SpringRmiServerApplication {
public static void main(String[] args)
{
//SpringApplication.run(SpringRmiServerApplication.class, args);
SpringApplication app = new SpringApplication(SpringRmiServerApplication.class);
app.setDefaultProperties(Collections.singletonMap("server.port", "8084"));
app.run(args);
}
}
Итак, моя проблема заключается вкак взаимодействовать чистый / обычный / стандартный Java-клиент RMI, который находится в Swing GUI с Spring RMI-сервер?
Edit # 1:
Кстати, если вы можетепредоставьте дальнейшие объяснения или ссылки о внутренних деталях создания заглушки Spring RMI и почему они не взаимодействуют, я буду счастлив.Спасибо, действительно.
А также, если вы посмотрите на мой метод getRemoteBean из устаревшего кода, как работает эта строка поиска?Я имею в виду, где файл реестра rmi или что-то находится на сервере, или это формат по умолчанию, или я могу настроить его?
Edit # 2: Я также пробовал этот вид поиска вclient:
private void initializeSpringEJBz(RemoteInvocation remoteInvocation) throws Exception {
HelloWorldRMI helloWorldService = (HelloWorldRMI) Naming.lookup("rmi://xxx:1099/helloworldrmi");
System.out.println("Output" + helloWorldService.sayHelloRmi("hello "));
//hw = remoteInvocation.getRemoteObject(HelloWorldRMI.class, "helloworldrmi");
}
Edit # 3:
Пока я искал, я обнаружил, что кто-то на весеннем форуме предложил принудить spring создать простой Java RMIзаглушка, мы должны внести некоторые изменения на стороне сервера, поэтому я попробовал это:
import java.rmi.server.RemoteObject;
public interface HelloWorldRMI extends **Remote** {
public String sayHelloRmi(String msg) throws **RemoteException**;
...
}
...
public class HelloWorldRMIimpl extends **RemoteObject** implements HelloWorldRMI {
...
}
Правильный ли код приведен выше для решения проблемы?
Кроме первой проблемыэто настройка соединения, как вы можете видеть в начале вопроса.Почему я получаю эту ошибку?В чем разница между "rmi: //" и "remote: //"?