จะต่างกับ 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 contextConfigLocation classpath: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