Convert interface to RMI interface - java

I have an interface, for example:
interface MyService {
String sayHello(Object who) throws ServiceException;
}
And now I need use this interface with RMI. Of course I can define another interface like:
interface MyService extends Remote{
String sayHello(Object who) throws RemoteException;
}
and then adapt my implemention class to the RMI interface.
But I have hundreds interfaces to convert to RMI. Write hundreds classes with same logic is so boring and ugly.
So is there a simple way to use these interfaces in RMI?

This is real scene in my work. And I used dynamic bytecode to do the hundurds work in one time.
First, here is my old code demo (without RMI).
class ServiceException extends Exception {
public ServiceException(String msg) {
super(msg);
}
}
interface MyService {
String sayHello(Object who) throws ServiceException;
void throwIt() throws ServiceException;
}
class MyServiceImpl implements MyService {
#Override
public String sayHello(Object who) throws ServiceException {
String hello = who.toString();
System.out.println("Server said: " + hello);
return "Hello! " + hello;
}
#Override
public void throwIt() throws ServiceException {
throw new ServiceException("throw in server");
}
}
And I use Javassist to convert the interface to RMI interface:
public static Class<? extends Remote> toRemoteInterface(Class<?> inter) throws Exception {
return cache("toRemote", inter, () -> uncheck(() -> {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.getAndRename(inter.getName(), inter.getName() + "$RemoteVersion");
cc.setModifiers(Modifier.PUBLIC | cc.getModifiers());
cc.addInterface(pool.get(Remote.class.getName()));
for (CtMethod cm : cc.getMethods()) {
cm.setExceptionTypes(new CtClass[] { pool.getCtClass(RemoteException.class.getName()) });
}
cc.writeFile();
return cc.toClass();
}));
}
Then, use Cglib to convert between the remote object and the local object:
public static <T> T fromRemote(Remote remote, Class<T> inter) throws Exception {
Enhancer e = new Enhancer();
e.setInterfaces(new Class[] { inter });
e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
Method remoteMethod = remote.getClass().getMethod(method.getName(), method.getParameterTypes());
try {
return remoteMethod.invoke(remote, args);
} catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException();
while (targetException instanceof RemoteException) {
targetException = targetException.getCause();
}
throw targetException;
}
});
return (T) e.create();
}
public static <T> Remote toRemote(T local, Class<T> inter) throws Exception {
Enhancer e = new Enhancer();
e.setSuperclass(UnicastRemoteObject.class);
e.setInterfaces(new Class[] { toRemoteInterface(inter) });
e.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
Method targetMethod = local.getClass().getMethod(method.getName(), method.getParameterTypes());
try {
return targetMethod.invoke(local, args);
} catch (InvocationTargetException ex) {
Throwable targetException = ex.getTargetException();
throw new RemoteException(targetException.getMessage(), targetException);
}
});
return (Remote) e.create();
}
Now we can use the non-rmi interface and its implementation just like it is RMI interface:
public static void startClient() throws Exception {
String stringURL = "rmi://127.0.0.1/" + MyService.class.getName();
toRemoteInterface(MyService.class);// define the Remote interface in client classloader
MyService service = fromRemote(Naming.lookup(stringURL), MyService.class);
String said = service.sayHello("Dean");
System.out.println("Client heard: " + said);
service.throwIt();
}
public static void startServer() throws Exception {
LocateRegistry.createRegistry(1099);
Remote remote = toRemote(new MyServiceImpl(), MyService.class);
Naming.rebind(MyService.class.getName(), remote);
System.out.println(remote);
System.out.println(remote.getClass());
System.out.println("Server started!");
}
For complete code, see this on github

Related

Java RMI doenst "go to" function of interface

Hello I am trying to create an RMI programm that sends and receives data from a server. However after I create my registries and look them up etc, and point my program to a method in the interface it throws itself into an integerpage (via debugger). my code is as follows
For the client
public static void SendData(int score,String name) throws RemoteException, NotBoundException {
try {
Registry reg = LocateRegistry.getRegistry("127.0.0.1",1099);
TestRemote remote = (TestRemote) reg.lookup("TestRMI");
remote.SendMyData(score,name);
} catch (Exception e) {
String ex = e.toString();
}
For the server
public class RMIserver {
public static void main(String[] args ) throws RemoteException, AlreadyBoundException {
RemoteImp imp = new RemoteImp();
Registry reg = LocateRegistry.createRegistry(1099);
reg.rebind("TestRMI", imp);
System.out.println("Server is Started");
}
}
Interface
public interface TestRemote extends Remote {
public ArrayList<TempPlayer> getOpdata() throws RemoteException;
public void SendMyData(int score, String player) throws RemoteException;}
And for the interface implementation:
private static final long serialVersionUID = 1L;
private ArrayList<TempPlayer> opdata = new ArrayList<TempPlayer>();
private ArrayList<String> lobbydata = new ArrayList<String>();
protected RemoteImp() throws RemoteException
{
}
#Override
public ArrayList<TempPlayer> getOpdata() throws RemoteException {
return opdata;
}
#Override
public void SendMyData(int score, String name) throws RemoteException {
//opdata.add(String.valueOf(score));
for (TempPlayer player : opdata) {
if (player.player == name) {
player.setScore(score);
} else {
opdata.add(new TempPlayer(score, name));
}
}
}
}
Now, when I try to invoke SendData by calling Remote.senddata, it goes to the Integer.java page. My question now is what could cause this and how do I get the program to actually go to the function I want it to go to?

Akka java never close over a ActorRef

I dont understand this statement about closing over the actor ref in the callback.
Currently I am using
public void onReceive(Object message) throws Exception {
ActorRef senderActorRef = getSender(); //never close over a future
if (message instanceof String) {
Future<String> f =akka.dispatch.Futures.future(new Callable<String>() {
public String call() {
String value= jedisWrapper.getString("name");
senderActorRef.tell((String) message,ActorRef.noSender());
return "what";
}
}, ex);
f.onSuccess(new OnSuccessExtension(), ex);
}
}
private final class OnSuccessExtension extends OnSuccess {
#Override
public void onSuccess(Object arg0) throws Throwable {
log.info("what");
}
}
Is this the right way to use it?
How can I pass the Sender Actor ref in the OnSuccess method?
Also whats the difference between onSuccess and OnComplete ?
If I want to use onComplete how would I use it?
Answer: Pass the Sender Actor Ref in the constructor. The answer given by another user.
OnSuccess is a specialized form of OnComplete.
OnComplete useage from Akka docs
final ExecutionContext ec = system.dispatcher();
future.onComplete(new OnComplete<String>() {
public void onComplete(Throwable failure, String result) {
if (failure != null) {
//We got a failure, handle it here
} else {
// We got a result, do something with it
}
}
}, ec);
Pass it in the constructor:
public void onReceive(Object message) throws Exception {
final ActorRef senderActorRef = getSender(); //never close over a future
if (message instanceof String) {
Future<String> f = // ...
f.onSuccess(new OnSuccessExtension(senderActorRef), ex);
}
}
private final class OnSuccessExtension extends OnSuccess {
private final ActorRef senderActorRef;
public OnSuccessExtension(ActorRef senderActorRef) {
this.senderActorRef = senderActorRef;
}
#Override
public void onSuccess(Object arg0) throws Throwable {
log.info("what");
// use senderActorRef
}
}

Retrying Method calls in generic way

My Java application requires a retry logic on remote calls failures.
These remote calls are:
scattered all over the application
pertain to different Remote Service classes.
Also, the retry logic may have varying retry interval and varying retry attempts.
I need a generic retry() implementation which can make appropriate method calls depending on from where it is called. Below is a simple code illustration of I am looking for. I know we can attempt to do this using java reflection, but, is there a framework or an open source available somewhere which is read-to-use?
try {
ClassA objA = remoteServiceA.call(paramA1, paramA2, ...);
} catch (Exception e){
ClassA objA = (ClassA)retry(remoteService, listOfParams, ..); // generic method call
}
..
try {
ClassB objB = remoteServiceB.call(paramB1, paramB2, ...);
} catch (Exception e){
ClassA objB = (ClassB)retry(remoteService, listOfParams, ..); // generic method call
}
As already suggested, you should use AOP and Java annotations. I would recommend a read-made mechanism from jcabi-aspects (I'm a developer):
#RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
return url.openConnection().getContent();
}
Read also this blog post: http://www.yegor256.com/2014/08/15/retry-java-method-on-exception.html
Update: Check RetryFunc from Cactoos.
This is a book example of where aspectj (or aop in general) can be used, see 8.2.7 Example in Spring documentation and 5 Reasons Java Developers Should Learn and Use AspectJ.
Basically an aspect intercepts all calls to given methods (specified using annotation, naming convention, whatever) and retries.
Assume you have a method, that need to retied at every 500ms and upto 5 times.
Current class:
public class RemoteCaller{
Service serviceCaller;
public void remoteCall(String message) {
serviceCaller.updateDetails( this.message);
return null;
}
}
Modified approach:
public class RetriableHelper<T> implements Callable<T> {
private Callable<T> task;
private int numberOfRetries;
private int numberOfTriesLeft;
private long timeToWait;
public RetriableHelper(int numberOfRetries, long timeToWait, Callable<T> task) {
this.numberOfRetries = numberOfRetries;
numberOfTriesLeft = numberOfRetries;
this.timeToWait = timeToWait;
this.task = task;
}
public T call() throws Exception {
while (true) {
try {
return task.call();
} catch (InterruptedException e) {
throw e;
} catch (CancellationException e) {
throw e;
} catch (Exception e) {
numberOfTriesLeft--;
if (numberOfTriesLeft == 0) {
throw e;
}
Thread.sleep(timeToWait);
}
}
}
}
Backend system/remote call class:
public class RemoteCaller{
Service serviceCaller;
public void remoteCall(String message) {
class RemoteCallable implements Callable<Void> {
String message;
public RemoteCallable( String message)
{
this.message = message;
}
public Void call() throws Exception{
serviceCaller.updateDetails( this.message);
return null;
}
}
RetriableHelper<Void> retriableHelper = new RetriableHelper<Void>(5, 500, new RemoteCallable( message));
try {
retriableHelper.call();
} catch (Exception e) {
throw e;
}
}
}
enter link description here Spring has a retry annotation which servers the purpose
Step 1: Add following dependency to your POM
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
Step 2: Enabling Spring Retry
To enable Spring Retry in an application, we need to add the #EnableRetry annotation to our #Configuration class:
Ex:
#Configuration
#EnableRetry
public class AppConfig { ... }
Step 3: To add retry functionality to methods, #Retryable can be used:
Ex:
#Service
public interface MyService {
#Retryable(
value = { SQLException.class },
maxAttempts = 2,
backoff = #Backoff(delay = 5000))
void retryService(String sql) throws SQLException;
...
}
Step 4.The #Recover annotation is used to define a separate recovery method when a #Retryable method fails with a specified exception:
Ex:
#Service
public interface MyService {
...
#Recover
void recover(SQLException e, String sql);
}
See Url for more details : http://www.baeldung.com/spring-retry
where do you get the services from? use a factory to Proxy the service you get from the original factory. The proxy can then implement the retry transparently. See the java Proxy/ProxyGenerators in reflection.
If you are using spring , then better go with Aspects.
Otherwise, below sample solution can work:
public class Test
{
public static void main(String[] args) throws Exception
{
Test test = new Test();
test.toRunFirst("Hello! This is normal invocation");
runWithRetry(test, "toRunFirst", "Hello! This is First, called with retry");
runWithRetry(test, "toRunSecond", "Hello! This is Second, called with retry");
}
public void toRunFirst(String s) {
System.out.println(s);
}
public void toRunSecond(String s) {
System.out.println(s);
}
public static Object runWithRetry(Object obj, String methodName, Object... args) throws Exception
{
Class<?>[] paramClass = new Class<?>[args.length];
for(int i=0; i< args.length; i++) {
paramClass[i] = args[i].getClass();
}
Method method = obj.getClass().getDeclaredMethod(methodName, paramClass);
int retryCount = 2;
for(int i=0; i< retryCount; i++) {
try {
return method.invoke(obj, args);
}
catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
I did not find what I needed so there is mine.
The main feature is that it throws the type of Exception you need when maxRetries is reached so you can catch it in the call.
import org.apache.log4j.Logger;
public class TaskUtils {
public static <E extends Throwable> void retry(int maxRetries, Task<E> task) throws E {
retry(maxRetries, 0, null, task);
}
public static <E extends Throwable> void retry(int maxRetries, long waitTimeMs, Logger logger, Task<E> task) throws E {
while (maxRetries > 0) {
maxRetries--;
try {
task.run();
} catch (Exception e) {
if (maxRetries == 0) {
try {
throw e;
} catch (Exception ignored) { // can't happen but just in case we wrap it in
throw new RuntimeException(e);
}
}
if (logger != null)
logger.warn("Attempt " + maxRetries + " failed", e);
try {
Thread.sleep(waitTimeMs);
} catch (InterruptedException ignored) {
}
}
}
}
public interface Task<E extends Throwable> {
void run() throws E;
}
}
Usage :
TaskUtils.retry(3, 500, LOGGER, () -> stmClickhouse.execute(
"ALTER TABLE `" + database + "`.`" + table.getName() + "` ON CLUSTER " + clusterName + allColumnsSql
));
add it into pom.xml
<dependency>
<groupId>org.deking.utils</groupId>
<artifactId>retry</artifactId>
<version>0.0.2-SNAPSHOT</version>
</dependency>
new Retry<String>()
.maxOperationWaitTime(30_000)//Max operation wait time during a single operation
.retryIntervalTime(1_000)//Interval time between two operations
.maxRetryTimes(3)//Retry times when operation failed(or timeout) at the first time
.operation(() -> {
//your operation
return "success!";
})
.judgement(t -> (t == null || t.isEmpty()))//add your judgement whether the operation should be retry(Operation should return a value)
.execute();
If you want add retry config annotation on method,and call it:
class RetryTests{
#RetryConfig( maxRetryTimes=1)
public static String TestAnnotation() {
return "aaa";
}
public static void main(String[] args) {
try {
new Retry<String>()
.of(RetryTest.class.getMethod("TestAnnotation"),null)
.judgement(r -> r.equals("aaa"))
.execute();
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

java - How to create an instance of generic type T

I am writing a server like below
public class Server<T extends RequestHandler> {
public void start() {
try{
this.serverSocket = new ServerSocket(this.port, this.backLog);
} catch (IOException e) {
LOGGER.error("Could not listen on port " + this.port, e);
System.exit(-1);
}
while (!stopTheServer) {
socket = null;
try {
socket = serverSocket.accept();
handleNewConnectionRequest(socket);
} catch (IOException e) {
LOGGER.warn("Accept failed at: " + this.port, e);
e.printStackTrace();
}
}
}
protected void handleNewConnectionRequest(Socket socket) {
try {
executorService.submit(new T(socket));
} catch (IOException e) {
e.printStackTrace();
}
}
}
But in the handleNewConnectionRequest(...) method, I can't create an instance of T as it is actually not a class. Also I can't use the method mentioned here as I want to pass the socket instance so that the request handler can get OutputStream and InputStream on the socket.
Can't I make a generic server like above and have different protocol handlers e.g
public class HttpRequestHandler extends RequestHandler {
...
}
public class FtpRequestHandler extends RequestHandler {
...
}
public class SmtpRequestHandler extends RequestHandler {
...
}
and then use them like below
Server<HttpRequestHandler> httpServer = new Server<HttpRequestHandler>();
Server<FtpRequestHandler> ftpServer = new Server<FtpRequestHandler >();
Server<SmtpRequestHandler> smtpServer = new Server<SmtpRequestHandler >();
You'll need an instance of the class. The generic type T isn't enough. So you'll do:
class Server <T extends RequestHandler> {
Class<T> clazz;
public Server(Class<T> clazz) {
this.clazz = clazz;
}
private T newRequest() {
return clazz.newInstance();
}
}
Maybe make different Server subclasses befitting various handler types. One example:
public class HttpServer extends Server<HttpRequestHandler> {
protected HttpRequestHandler wrapSocket(Socket socket) {
return new HttpRequestHandler(socket);
}
}
And adapt Server like so:
public abstract class Server<T extends RequestHandler> {
protected abstract T wrapSocket(Socket socket);
protected void handleNewConnectionRequest(Socket socket) {
try {
executorService.submit(wrapSocket(socket));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Just a thought...
You don't. It doesn't make sense. In this case I'd probably avoid generics. Plain old interface or abstract class does job. You can make abstract server with abstract factory method.
abstract class Server {
abstract protected RequestHandler newRequest(Socket socket);
... same as before
}
You can't do it directly like that with generics in Java. You can use Reflection if you get the actual class object of RequestHandler with getClass(). You could try saving the class of the item in constructor and then write helper method like this:
Save the class object (in constructor for example):
this.clazz = requestHandler.getClass()
Then create new object of same class:
E instantiate(Class<E> clazz)
{
return clazz.newInstance();
}
/* =================|Cassandra to Java Connection|===================== */
package demo_cassandra_connection;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
public class java_to_cassandra_connection
{
public static void main(String[] args)
{
com.datastax.driver.core.Session ses;
Cluster cluster=
Cluster.builder().addContactPoints("54.191.46.102",
"54.149.32.12", "54.191.43.254")
.withPort(9042).withCredentials("cassandra","cassandra").build();
ses = cluster.connect();
Session session = (Session) cluster.connect();
String cqlStatement = "SELECT * FROM testapp.user";
for (Row row : ((com.datastax.driver.core.Session)
session).execute(cqlStatement))
{
System.out.println(row.toString());
}
}
}

How to create 2 or more different RMI remote objects in one interface?(factory Pattern)

For Example
SvrInterface si1 = (SvrInterface) Naming.lookup(Address);
SvrInterface si2 = (SvrInterface) Naming.lookup(Address);
si1.setUser ("User1");
si2.setUser ("User2");
And Next
String si1User = si1.getUser();
Will the result of si1User become "User1" ?
The simple answer in your case is No. You're still referencing the same remote object in registry bound to the address. Good place to begin learning more about RMI architecture > here.
EDIT 1
Simple RMI Factory example I whipped up quickly...
EchoService
public interface EchoService extends Remote, Serializable{
String echo(String msg) throws RemoteException;
String getUser() throws RemoteException;
void setUser(String user) throws RemoteException;
}
EchoServiceImpl
public class EchoServiceImpl extends UnicastRemoteObject implements EchoService {
private static final long serialVersionUID = -3671463448485643888L;
private String user;
public EchoServiceImpl() throws RemoteException {
super();
}
#Override
public String echo(String msg) throws RemoteException {
return this.user + ": " + msg;
}
#Override
public String getUser() throws RemoteException {
return this.user;
}
#Override
public void setUser(String user) throws RemoteException {
this.user = user;
}
}
EchoServiceFactory
public interface EchoServiceFactory extends Remote {
EchoService createEchoService() throws RemoteException;
}
EchoServiceFactoryImpl
public class EchoServiceFactoryImpl extends UnicastRemoteObject implements
EchoServiceFactory {
private static final long serialVersionUID = 6625883990856972736L;
protected EchoServiceFactoryImpl() throws RemoteException {
super();
System.setProperty("java.rmi.server.codebase", EchoServiceFactory.class.getProtectionDomain().getCodeSource().getLocation().toString());
System.setProperty("java.security.policy", "/java.policy");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
try {
Registry registry = LocateRegistry.getRegistry("localhost");
registry.rebind("EchoService", this);
System.out.println("Echo service factory registered.");
} catch (Exception e) {
System.err.println("Error registering echo service factory: "
+ e.getMessage());
}
}
#Override
public EchoService createEchoService() throws RemoteException {
return new EchoServiceImpl();
}
public static void main(String[] args) {
try {
new EchoServiceFactoryImpl();
} catch (RemoteException e) {
System.err.println("Error creating echo service factory: "
+ e.getMessage());
}
}
}
EchoServiceClient
public class EchoServiceClient {
public static void main(String[] args) {
try {
System.setProperty("java.security.policy", "/java.policy");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new SecurityManager());
}
Registry registry = LocateRegistry.getRegistry("localhost");
EchoServiceFactory serviceFactory =
(EchoServiceFactory) registry.lookup("EchoService");
EchoService echoServiceX = serviceFactory.createEchoService();
echoServiceX.setUser("Tom");
System.out.println(echoServiceX.echo("Hello!"));
EchoService echoServiceY =
serviceFactory.createEchoService();
echoServiceY.setUser("Jerry");
System.out.println(echoServiceY.echo("Hello"));
System.out.println(echoServiceX.echo("Hey There!!!"));
System.out.println(echoServiceY.echo(":o)"));
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
Running the client produce output as below.
Tom: Hello!
Jerry: Hello
Tom: Hey There!!!
Jerry: :o)

Categories

Resources