วันพุธที่ 12 พฤษภาคม พ.ศ. 2553

rmi remote client on spring framework by annotation

หลังจากที่ได้ทำ service server rmi ไปแล้ว เราก็ไปทำที่ client บ้างดีกว่า concept
จะต่างกับ server นิดหน่อยคือ service มีกี่ service ก็จะประกาศเท่าจำนวน แต่ clinet
นั้นเนื่องจากเราเอา Annotation ไปแปะไว้ตอนเรียกที่ต่าง ๆ ทำให้เราต้องมีการ check
ก่อนว่า service ที่เราเรียกนั้น มันเคยถูกประกาศไว้หรือยัง ฟังแล้วอาจงง ไปเริ่มดีกว่า

บทความก่อนหน้า
rmi remote server on spring framework by annotation

file แรก เช่นเดิมเลยคือ
project\WebContent\WEB-INF\web.xml

    TestRmiAnnoClient
    
        index.html
        index.htm
        index.jsp
        default.html
        default.htm
        default.jsp
    
    
        contextConfigLocationclasspath:conf/applicationContext.xml
    
        org.springframework.web.context.ContextLoaderListener
    


ต่อไปก็ config file
project\src\conf\applicationContext.xml



    <context:annotation-config/>
    <context:component-scan base-package="com.company.project.service" />
    <import resource="/rmiContext.xml" />



แยกในส่วน rmi มาเป็นอีก file
project\src\conf\rmiContext.xml




    
        <property name="basePackage" value="com.company.project.service" />
        <property name="host" value="localhost" />
        <property name="port" value="1019" />
        <property name="refreshStubOnConnectFailure" value="true" />
        <property name="cacheStub" value="false" />
        <property name="lookupStubOnStartup" value="false" />
    



file Annotation ที่นำไปใช้ขอเรียก service
project\src\com\company\spring\remoting\RmiClient.java
package com.company.spring.remoting;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;

@Target( { ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RmiClient {

}

file class ที่ทำการเรียก service มาผูก ด้วยการ call rmi
project\src\com\company\spring\remoting\RmiClientAnnotationPostProcessor.java
package com.company.spring.remoting;

import java.lang.reflect.Field;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.remoting.rmi.RmiProxyFactoryBean;

public class RmiClientAnnotationPostProcessor implements
  BeanFactoryPostProcessor, BeanClassLoaderAware {

 private List<Class<?>> remoteServiceInterfaces;

 private String host;

 private int port;

 private String basePackage;

 private ClassLoader classLoader;

 private boolean refreshStubOnConnectFailure;

 private boolean cacheStub;

 private boolean lookupStubOnStartup;

 @Override
 public void postProcessBeanFactory(
   ConfigurableListableBeanFactory beanFactory) throws BeansException {
  System.out
    .println(" RmiClientAnnotationPostProcessor >> postProcessBeanFactory ");

  for (String beanName : beanFactory.getBeanDefinitionNames()) {
   BeanDefinition beanDefinition = beanFactory
     .getBeanDefinition(beanName);

   System.out.println(" BeanDefinitionNames : " + beanName);

   // Ignore abstract bean!
   if (beanDefinition.isAbstract()
     || beanDefinition.getBeanClassName() == null
     || beanDefinition.getBeanClassName().indexOf(basePackage) == -1) {
    continue;
   }
   try {
    Class<?> clazz = Class.forName(beanDefinition
      .getBeanClassName());

    Field[] fields = clazz.getDeclaredFields();

    for (Field field : fields) {
     RmiClient client = field.getAnnotation(RmiClient.class);

     if (client == null) {
      continue;
     }
     Class<?> remoteServiceInterface = field.getType();

     RmiProxyFactoryBean clientBean = getRmiProxyFactoryBean(remoteServiceInterface);

     String clientBeanName = getClientName(field.getName());

     if (!beanFactory.containsSingleton(clientBeanName)) {
      beanFactory.registerSingleton(clientBeanName,
        clientBean);

      System.out.println("Register rmi client: " + clientBean
        + " " + "with name '" + clientBeanName
        + "' success.");
     }

    }
   } catch (ClassNotFoundException e) {
    throw new FatalBeanException("No class for name " + beanName, e);
   }

  }
 }

 private String getClientName(String remoteServiceInterfaceName) {
  return StringUtils.uncapitalize(remoteServiceInterfaceName) + "Client";
 }

 private RmiProxyFactoryBean getRmiProxyFactoryBean(
   Class<?> remoteServiceInterface) {
  RmiProxyFactoryBean client = new RmiProxyFactoryBean();
  System.out.println(" RmiClientAnnotationPostProcessor >> getRmiProxyFactoryBean");
  System.out.println(" >> RemoteServiceInterface name : " + remoteServiceInterface.getSimpleName());
  System.out.println(" >> RemoteServiceInterface host : " + host);
  System.out.println(" >> RemoteServiceInterface port : " + port);
  client.setServiceInterface(remoteServiceInterface);
  client.setServiceUrl("rmi://" + host + ":" + port + "/"
    + remoteServiceInterface.getSimpleName());

  client.setRefreshStubOnConnectFailure(refreshStubOnConnectFailure);
  client.setCacheStub(cacheStub);
  client.setLookupStubOnStartup(lookupStubOnStartup);
  client.setBeanClassLoader(classLoader);

  client.afterPropertiesSet();

  return client;
 }

 public void setRemoteServiceInterfaces(
   List<Class<?>> remoteServiceInterfaces) {
  this.remoteServiceInterfaces = remoteServiceInterfaces;
 }

 public List<Class<?>> getRemoteServiceInterfaces() {
  return remoteServiceInterfaces;
 }

 public String getBasePackage() {
  return basePackage;
 }

 public void setBasePackage(String basePackage) {
  this.basePackage = basePackage;
 }

 public String getHost() {
  return host;
 }

 public void setHost(String host) {
  this.host = host;
 }

 public int getPort() {
  return port;
 }

 public void setPort(int port) {
  this.port = port;
 }

 @Override
 public void setBeanClassLoader(ClassLoader classLoader) {
  this.classLoader = classLoader;
 }

 public boolean isRefreshStubOnConnectFailure() {
  return refreshStubOnConnectFailure;
 }

 public void setRefreshStubOnConnectFailure(
   boolean refreshStubOnConnectFailure) {
  this.refreshStubOnConnectFailure = refreshStubOnConnectFailure;
 }

 public boolean isCacheStub() {
  return cacheStub;
 }

 public void setCacheStub(boolean cacheStub) {
  this.cacheStub = cacheStub;
 }

 public boolean isLookupStubOnStartup() {
  return lookupStubOnStartup;
 }

 public void setLookupStubOnStartup(boolean lookupStubOnStartup) {
  this.lookupStubOnStartup = lookupStubOnStartup;
 }
}


========================================
ส่วนต่อไปคือเริ่มทำ service ที่ใช้งานจริงๆ กันเลยดีกว่าเริ่มด้วย
Interface rmi server service ตัวนี้คือของ interface server ตอนนำไปใช้งานจริง
คงถูก pack เป็น Jar file สำหรับใช้งาน แต่ตอนนี้ แค่ copy มาไว้ก่อนเพื่อความง่าย
project\src\com\company\project\service\ItestService.java
package com.company.project.service;

public interface ItestService {
 public String getPrefixHello(String str);
}

Interface service
project\src\com\company\project\service\IServiceNormal.java
package com.company.project.service;

public interface IServiceNormal {
 public String getTestRmiClientCall(String str);
}

Implements service
project\src\com\company\project\service\ServiceNormal.java
package com.company.project.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.company.spring.remoting.RmiClient;

@Service
public class ServiceNormal implements IServiceNormal {
 
 @Autowired
 @RmiClient
 private ItestService itestService;

 public void setItestService(ItestService itestService) {
  this.itestService = itestService;
 }

 @Override
 public String getTestRmiClientCall(String str) {
  System.out
  .println(" ServiceNormal >> getTestRmiClientCall ");
  String outStr = itestService.getPrefixHello(str);
  System.out
  .println(" result : "+outStr);
  return outStr;
 } 
}

หลังจากนั้นก็ลอง test service ดูโดยทำตัว test ขึ้นมา (ตาม step เมพขิง ๆ)
project\src\com\company\project\test\TestServiceNormal.java
package com.company.project.test;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.company.project.service.IServiceNormal;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/conf/applicationContext.xml"})
public class TestServiceNormal {
 
 private IServiceNormal iServiceNormal;
 
 @Autowired
 public void setiServiceNormal(IServiceNormal iServiceNormal) {
  this.iServiceNormal = iServiceNormal;
 }

 @Before
 public void setUp() throws Exception {
  System.out.println("setUp");
 }

 @After
 public void tearDown() throws Exception {
  System.out.println("tearDown");
 }
 
 @Test
 public void testServiceNormal(){
  iServiceNormal.getTestRmiClientCall(" test service client ...");
 }

}

log ที่ได้หลังจาก run test
12 พ.ค. 2553 11:34:08 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class com.company.project.test.TestServiceNormal]: using defaults.
12 พ.ค. 2553 11:34:08 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
12 พ.ค. 2553 11:34:09 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/rmiContext.xml]
12 พ.ค. 2553 11:34:09 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@118f375: startup date [Wed May 12 11:34:09 ICT 2010]; root of context hierarchy
RmiClientAnnotationPostProcessor >> postProcessBeanFactory
BeanDefinitionNames : org.springframework.context.annotation.internalConfigurationAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalAutowiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalRequiredAnnotationProcessor
BeanDefinitionNames : org.springframework.context.annotation.internalCommonAnnotationProcessor
BeanDefinitionNames : serviceNormal
RmiClientAnnotationPostProcessor >> getRmiProxyFactoryBean
>> RemoteServiceInterface name : ItestService
>> RemoteServiceInterface host : localhost
>> RemoteServiceInterface port : 1019
Register rmi client: org.springframework.remoting.rmi.RmiProxyFactoryBean@1d2fc36 with name 'itestServiceClient' success.
BeanDefinitionNames : com.company.spring.remoting.RmiClientAnnotationPostProcessor#0
12 พ.ค. 2553 11:34:09 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1016632: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,serviceNormal,com.company.spring.remoting.RmiClientAnnotationPostProcessor#0]; root of factory hierarchy
setUp
ServiceNormal >> getTestRmiClientCall
result : Hello 123 : test service client ...
tearDown

12 พ.ค. 2553 11:34:10 org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@118f375: startup date [Wed May 12 11:34:09 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:34:10 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1016632: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,serviceNormal,com.company.spring.remoting.RmiClientAnnotationPostProcessor#0]; root of factory hierarchy

log ที่ได้หลังจาก run test บน server เพื่อให้เห็นว่ามีการ request มายัง server ได้
TestService >> getPrefixHello

ไม่มีความคิดเห็น: