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

rmi remote server on spring framework by annotation

rmi คือ การ remote service ข้ามเครื่องวิธีหนึ่ง ส่วนใหญ่จะใช้ในกรณีแยก service ออกจาก interface เพื่อ

1. ความปลอดภัย คือ ทำให้สามารถควบคุมการเข้าถึง การเรียกใช้ และการดึงข้อมูลที่สำคัญ หรือเป็นความลับ
รวมถึงการกำหนดข้อจำกัดต่าง ๆ

2. การทำเป็น service กลาง ซึ่งทำให้ไม่ต้องเขียน service ขึ้นมาใหม่บ่อย ๆ ในรูปแบบงานที่คล้าย ๆ กันเกิดขึ้นใหม่
และเป็นการแยก interface ทำให้สามารถ แก้ไข หรือ เปลีี่ยนแปลง interface ได้ตลอดเวลาโดยไม่กระทบ

ที่เหลือคิดไม่ออก แล้ว ไว้ว่าง ๆ มาเขียนข้อดีเพิ่มให้ครับ ^^'

ตัวอย่างโดยใช้ xml ทำการ config บน spring framework
http://static.springsource.org/spring/docs/2.0.4/reference/remoting.html

บทความถัดไป
rmi remote client on spring framework by annotation

เรามาเปลี่ยนจาก xml เป็น annotation กัน เริ่มจาก
project/WebContent/WEB-INF/web.xml


    MUWAHOBack
    
        contextConfigLocationclasspath:conf/applicationContext.xml
    
        org.springframework.web.context.ContextLoaderListener
    
    
        org.springframework.web.context.request.RequestContextListener
    
    
        30
    
    
        index.jsp
    


มาถึง config service file
project\src\conf\applicationContext.xml




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



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




    
        
        
    

    
    

    
        
    



มาถึงส่วนสำคัญในการประกาศ service rmi
ส่วนแรกจะเป็น Application Context ที่ประกาศเป็นตัวเริ่มต้น เพื่อให้ reuse ได้
project\src\com\company\spring\context\ApplicationContextProvider.java
package com.company.spring.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public abstract class ApplicationContextProvider implements
  ApplicationContextAware, BeanFactoryPostProcessor, BeanPostProcessor,
  DisposableBean {

 private ApplicationContext context;

 public ApplicationContext getContext() {
  return context;
 }

 @Override
 public void setApplicationContext(ApplicationContext context)
   throws BeansException {
  this.context = context;
 }
}

ต่อมาเป็นในส่วนที่ทำเพื่อประกาศ Annotation เราจะนำ Annotation ตัวนี้ไปแปะไว้
บน Class implement ที่เราต้องการประกาศให้เป็น service rmi
project\src\com\company\spring\remoting\RmiServer.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.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RmiServer {
 
 String name() default "";
 
 Class<?> serviceInterface();
}

ส่วนต่อไป ทำการผูก service ที่ประกาศ Annotation @RmiServer เพื่อประกาศ
service ให้ใช้บริการ rmi ได้โดยผูกเข้ากับ Bean factory ของ Spring
project\src\com\company\spring\remoting\RemoteAnnotationBeanFactoryPostProcessor.java
package com.company.spring.remoting;

import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.List;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.remoting.rmi.RmiServiceExporter;
import org.springframework.remoting.support.RemoteExporter;
import com.company.spring.context.ApplicationContextProvider;

public class RemoteAnnotationBeanFactoryPostProcessor extends
  ApplicationContextProvider {

 private String basePackage;

 private List<String> remoteExporterNames = new ArrayList<String>();

 @Override
 public Object postProcessBeforeInitialization(Object obj, String beanName)
   throws BeansException {
  System.out
    .println(" RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization ");
  System.out.println(" >> Bean name : " + beanName);
  System.out.println(" >> Class name : " + obj);
  return obj;
 }

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

  for (String beanName : beanFactory.getBeanDefinitionNames()) {

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

   BeanDefinition beanDefinition = beanFactory
     .getBeanDefinition(beanName);

   // Ignore abstract bean! and empty beans
   if (beanDefinition.isAbstract()
     || beanDefinition.getBeanClassName() == null
     || beanDefinition.getBeanClassName().indexOf(basePackage) == -1) {
    continue;
   }

   try {
    RmiServer remote = AnnotationUtils.findAnnotation(Class
      .forName(beanDefinition.getBeanClassName()),
      RmiServer.class);

    if (remote == null) {
     continue;
    }

    String remoteExporterName = getRmiServiceBeanName(remote);
    System.out.println(" Register Singleton RemoteExporterName : "
      + remoteExporterName);
    RemoteExporter remoteExporter = createRemoteExporter(remote);

    beanFactory.registerSingleton(remoteExporterName,
      remoteExporter);

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

 private String getRmiServiceBeanName(RmiServer remote) {
  return "/" + remote.serviceInterface().getSimpleName();
 }

 private RemoteExporter createRemoteExporter(RmiServer remote) {
  RmiServiceExporter remoteExporter = new RmiServiceExporter();

  remoteExporter.setServiceInterface(remote.serviceInterface());
  remoteExporter
    .setServiceName(remote.serviceInterface().getSimpleName());

  return remoteExporter;
 }

 @Override
 public Object postProcessAfterInitialization(Object obj, String beanName)
   throws BeansException {
  System.out
    .println(" RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization ");
  System.out.println(" >> Bean name : " + beanName);
  System.out.println(" >> Class name : " + obj);
  Class<?> targetClass = null;
  Object _obj = obj;

  while (_obj instanceof Advised) {
   Advised advised = (Advised) _obj;
   try {
    _obj = advised.getTargetSource().getTarget();
   } catch (Exception e) {
    throw new FatalBeanException(
      "Exception initializing RmiServiceExporter", e);
   }
  }

  targetClass = _obj.getClass();

  RmiServer remote = AnnotationUtils.findAnnotation(targetClass,
    RmiServer.class);

  if (remote == null) {
   return obj;
  }
  try {

   RmiServiceExporter remoteExporter = (RmiServiceExporter) getContext()
     .getBean(getRmiServiceBeanName(remote));
   remoteExporter.setService(obj);

   Registry registry = getContext().getBean("rmiRegistry",
     Registry.class);
   remoteExporter.setRegistry(registry);
   remoteExporter.afterPropertiesSet();

   System.out.println("Create remote service success " + "with name "
     + remote.serviceInterface().getSimpleName()
     + " with RMI registry: " + registry + ".");
  } catch (Exception e) {
   throw new FatalBeanException(
     "Exception initializing RmiServiceExporter", e);
  }

  return obj;
 }

 @Override
 public void destroy() throws Exception {
  System.out
    .println(" RemoteAnnotationBeanFactoryPostProcessor >> destroy ");
  for (String remoteExporterName : remoteExporterNames) {
   getContext().getBean(remoteExporterName, RmiServiceExporter.class)
     .destroy();
  }
 }

 /*
  * auto-create get-set method.
  */
 public String getBasePackage() {
  return basePackage;
 }

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

 public List<String> getRemoteExporterNames() {
  return remoteExporterNames;
 }

 public void setRemoteExporterNames(List<String> remoteExporterNames) {
  this.remoteExporterNames = remoteExporterNames;
 }
}


========================================
ส่วนต่อไปคือเริ่มทำ service ที่ใช้งานจริงๆ กันเลยดีกว่าเริ่มด้วย
Interface service
project\src\com\company\project\service\ItestService.java
package com.company.project.service;

public interface ItestService {
 public String getPrefixHello(String str);
}
project\src\com\company\project\service\TestService.java
Implements service
package com.company.project.service;

import org.springframework.stereotype.Service;
import com.company.spring.remoting.RmiServer;

@RmiServer(serviceInterface = ItestService.class)
@Service
public class TestService implements ItestService {

 @Override
 public String getPrefixHello(String str) {
  System.out.println(" TestService >> getPrefixHello ");
  return "Hello 123 : "+str;
 }

}

หลังจากนั้นก็ลอง test service ดูโดยทำตัว test ขึ้นมา
project\src\com\company\project\test\TestServiceTest.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.ItestService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/conf/applicationContext.xml"})
public class TestServiceTest {
 
 @Autowired
 private ItestService itestService;
 
 public void setItestService(ItestService itestService) {
  this.itestService = itestService;
 }

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

 @After
 public void tearDown() throws Exception {
  System.out.println("tearDown");
 }
 
 @Test
 public void testGetData() {
  System.out.println("testGetData");
  String outStr = "";
  try{
   outStr = this.itestService.getPrefixHello("test 123 d^^");
   System.out.println("result : " + outStr);
  }catch(Exception e){
   System.out.println("ERROR : " + e);
   e.printStackTrace();
  }
 }
}

log ที่ได้หลังจาก run test
12 พ.ค. 2553 11:06:38 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class com.company.project.test.TestServiceTest]: using defaults.
12 พ.ค. 2553 11:06:38 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
12 พ.ค. 2553 11:06:39 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/rmiContext.xml]
12 พ.ค. 2553 11:06:39 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@7ffe01: startup date [Wed May 12 11:06:39 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:06:39 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Looking for RMI registry at port '1019'
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Could not detect RMI registry - creating new one
RemoteAnnotationBeanFactoryPostProcessor >> 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 : testService
Register Singleton RemoteExporterName : /ItestService
BeanDefinitionNames : rmiRegistry
BeanDefinitionNames : appContextProvider
BeanDefinitionNames : com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0
12 พ.ค. 2553 11:06:40 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@ff057f: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testService,rmiRegistry,appContextProvider,com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0]; root of factory hierarchy
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@15a6029
RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@15a6029
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiServiceExporter prepare
INFO: Binding service 'ItestService' to RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]]
Create remote service success with name ItestService with RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]].
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization
>> Bean name : com.company.project.test.TestServiceTest
>> Class name : com.company.project.test.TestServiceTest@f9c40
RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization
>> Bean name : com.company.project.test.TestServiceTest
>> Class name : com.company.project.test.TestServiceTest@f9c40
setUp
testGetData
TestService >> getPrefixHello
result : Hello 123 : test 123 d^^
tearDown

12 พ.ค. 2553 11:06:40 org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@7ffe01: startup date [Wed May 12 11:06:39 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:06:40 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@ff057f: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testService,rmiRegistry,appContextProvider,com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0]; root of factory hierarchy
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiServiceExporter destroy
INFO: Unbinding RMI service 'ItestService' from registry at port '1099'
12 พ.ค. 2553 11:06:40 org.springframework.remoting.rmi.RmiRegistryFactoryBean destroy
INFO: Unexporting RMI registry
RemoteAnnotationBeanFactoryPostProcessor >> destroy

ลอง run บน env server tomcat6
12 พ.ค. 2553 11:06:01 org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:TestRmiAnnoServer' did not find a matching property.
12 พ.ค. 2553 11:06:01 org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jre6\bin;.;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:/Program Files/Java/jre6/bin/client;C:/Program Files/Java/jre6/bin;C:\Program Files\CA\SC\CAWIN\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\system32\wbem;.;C:\Program Files\Java\jdk1.6.0_11\bin;C:\Program Files\Common Files\NetSarang;C:\Program Files\Java\jdk1.6.0_11\BIN;C:\ant\bin;C:\Oracle9i\bin;C:\Program Files\Oracle\jre\1.3.1\bin;C:\Program Files\Oracle\jre\1.1.8\bin;C:\Ora6i\bin;C:\ORANT\bin;C:\TUXEDO\bin;C:\Program Files\SQLLIB\BIN;C:\Program Files\SQLLIB\HELP;C:\Ora6i\jdk\bin;C:\ORAWIN\Bin;C:\Program Files\cvsnt;;C:\Program Files\Common Files\Compuware;C:\PROGRA~1\CA\SC\CAM\bin;C:\Program Files\CA\DSM\bin;C:\ORAWIN\BIN;C:\ORANT\BIN;C:\CUBICS3T;C:\TUXEDO\BIN;
12 พ.ค. 2553 11:06:01 org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
12 พ.ค. 2553 11:06:01 org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 801 ms
12 พ.ค. 2553 11:06:01 org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
12 พ.ค. 2553 11:06:01 org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.18
12 พ.ค. 2553 11:06:02 org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring root WebApplicationContext
12 พ.ค. 2553 11:06:02 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
12 พ.ค. 2553 11:06:02 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Wed May 12 11:06:02 ICT 2010]; root of context hierarchy
12 พ.ค. 2553 11:06:03 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
12 พ.ค. 2553 11:06:03 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/rmiContext.xml]
12 พ.ค. 2553 11:06:04 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Looking for RMI registry at port '1019'
12 พ.ค. 2553 11:06:05 org.springframework.remoting.rmi.RmiRegistryFactoryBean getRegistry
INFO: Could not detect RMI registry - creating new one
RemoteAnnotationBeanFactoryPostProcessor >> 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 : testService
Register Singleton RemoteExporterName : /ItestService
BeanDefinitionNames : rmiRegistry
BeanDefinitionNames : appContextProvider
BeanDefinitionNames : com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0
12 พ.ค. 2553 11:06:05 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1e1dadb: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testService,rmiRegistry,appContextProvider,com.company.spring.remoting.RemoteAnnotationBeanFactoryPostProcessor#0]; root of factory hierarchy
RemoteAnnotationBeanFactoryPostProcessor >> postProcessBeforeInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@1777b1
RemoteAnnotationBeanFactoryPostProcessor >> postProcessAfterInitialization
>> Bean name : testService
>> Class name : com.company.project.service.TestService@1777b1
12 พ.ค. 2553 11:06:05 org.springframework.remoting.rmi.RmiServiceExporter prepare
INFO: Binding service 'ItestService' to RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]]
Create remote service success with name ItestService with RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[10.239.20.141:1019](local),objID:[0:0:0, 0]]]].
12 พ.ค. 2553 11:06:05 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 2516 ms
12 พ.ค. 2553 11:06:05 org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8080
12 พ.ค. 2553 11:06:05 org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
12 พ.ค. 2553 11:06:05 org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/47 config=null
12 พ.ค. 2553 11:06:05 org.apache.catalina.startup.Catalina start
INFO: Server startup in 4107 ms

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