วันพุธที่ 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

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

วันอังคารที่ 11 พฤษภาคม พ.ศ. 2553

note_2010-05-11

11:25
เช้าวันนี้ก็มาทำงานตามปกติ แต่ปลั๊กกลับไม่มีไฟซะงั้น กว่าจะใช้ได้ก็ 9:30 น

ได้เห็นเกมส์ StarCraft 2 แล้วก็อยากกลับไปเล่นอีกที ^^'

วันนี้ก็มาทำ RMI เป็น Annotation บน Spring Framework กันต่อ ทำไปก็งงไป
แกะจากพี่ไวท์อีกที ในฝั่ง server แกะโครงมาได้และ ยังไม่ได้ test เพราะ
รอในส่วน client ที่กำลังทำเนีี่ยแหละ ว่าแต่ concept ต่างกันนิดหน่อย -_-'
แต่คงต้องรีบทำให้เสร็จภายในอาทิตย์นี้สินะ รู้สึกใยแมงมุม และสนิกขึ้น
ทำงานสบายเกินช่วงหลัง T_T' เลยเีสียนิสัยเลยว่างั้น

ถ้าเขียนเสร็จคงได้เอามาแปะหัวข้อเพิ่มอีก แต่ตอนนี้ งม ๆ ๆ ไปก่อน

ว่าแต่ช่วงนี้คงลด Facebook ลงมาแระ เล่นเยอะชักไม่ได้ทำอะไร
ต้องเริ่มเอาชนะใจตัวเองอีกครั้ง ^^y

วันพฤหัสบดีที่ 6 พฤษภาคม พ.ศ. 2553

java web-service with cxf(2.2.7) on spring framework

ตกลงก็มามอง cxf ในการทำ web-service ทั้ง client และ server เพราะความง่าย อะอย่างพล่ามมากเลย เริ่มดีกว่า
เราก็ต้อง create project เป็น web อยู่แล้ว ในที่นี้ก็คงต้องเดินไปหา
project/WebContent/WEB-INF/web.xml แล้วก็ละเลงตามนี้




    Auth Manager

    
        contextConfigLocationclasspath:com/company/auth/service/cxf.xml
    
        org.springframework.web.context.ContextLoaderListener
    
    
        CXFServlet
        org.apache.cxf.transport.servlet.CXFServlet
    
    
        CXFServlet
        /services/*
    



ต่อไป model ตัวที่จะลาก parameter ต่าง ๆ ไป ๆ มาก ๆ
project/src/com/company/auth/bean/Employee.java
package com.company.auth.bean;

import java.io.Serializable;
import java.util.Set;

public class Employee implements Serializable {
 
 private static final long serialVersionUID = 1L;
 private String gid;
 private String lastName;
 private String firstName;
 private Set privileges;
 
 public Employee() {}

 public String getGid() {
  return gid;
 }
 public void setGid(String gid) {
  this.gid = gid;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public Set getPrivileges() {
  return privileges;
 }
 public void setPrivileges(Set privileges) {
  this.privileges = privileges;
 }
 
 public boolean isUserInRole(String role) {
  if(privileges == null) { return false; }
  else { return privileges.contains(role); }
 }
}

เริ่มด้วยการประกาศ service บน xml
project/src/com/company/auth/service/cxf.xml


    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
    <jaxws:endpoint id="auth"
      implementor="com.company.auth.service.AuthServiceImpl"
      address="/cxfAuth"/>


ต่อมาก็เริ่มในส่วน service ที่จะเป็นตัวถูกเรียกใช้ง่าน
project/src/com/company/auth/service/AuthService.java
package com.company.auth.service;

import javax.jws.WebParam;
import javax.jws.WebService;
import com.company.auth.bean.Employee;

@WebService
public interface AuthService {
 Employee getEmployee(@WebParam(name="gid") String gid);
}

project/src/com/company/auth/service/AuthServiceImpl.java
package com.company.auth.service;

import javax.jws.WebService;
import com.company.auth.bean.Employee;

@WebService(endpointInterface = "com.company.auth.service.AuthService", serviceName = "corporateAuthService")
public class AuthServiceImpl implements AuthService{

 @Override
 public Employee getEmployee(String gid) {
  System.out.print("test log for apache cxf web-service... input gid : " + gid);
  return new Employee();
 }
}

หลังจากที่ทำการเขียน service ขึ้นมาเสร็จเรียบร้อยแล้วก็สั่ง run server รอไว้เลย
แล้วเราก็มาเขียน client แบบง่าย ๆ เพื่อลองเรียกใช้กัน(จริง ๆ แล้วเขียนไว้ที่ใหนก็ได้
แต่ต้องนำ model กับ interface มาด้วย แต่ในที่นี้เอาง่าย ๆ ไว้ก่อน)
project/src/com/company/auth/client/Client.java
package com.company.auth.client;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import com.company.auth.bean.Employee;
import com.company.auth.service.AuthService;

public class Client {

    private Client() {
    } 

    public static void main(String args[]) throws Exception {

        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

        factory.getInInterceptors().add(new LoggingInInterceptor());
        factory.getOutInterceptors().add(new LoggingOutInterceptor());
        factory.setServiceClass(AuthService.class);
        factory.setAddress("http://localhost:8080/Test1/services/cxfAuth");
        AuthService client = (AuthService) factory.create();

        Employee employee = client.getEmployee("0223938");
        System.out.println("Server said: " + employee.getLastName() + ", " + employee.getFirstName());
        System.exit(0);

    }
}

ในส่วน ของ wsdl จะเข้าไปที่
http://localhost:8080/project/services/cxfAuth?wsdl

กรณีที่เราต้องการเขียนเฉพาะ client ให้เราไปมอง server ที่ตัว file wsdl แล้วใช้
wsdl2java

โดยเมื่อ download cxf มา จะได้ file apache-cxf-2.2.7.zip คำสั่งจะอยู่ที่
apache-cxf-2.2.7\bin\wsdl2java.bat

ก็ให้เราใช้คำสั่ง ตามหลังด้วย url ของ wsdl ได้เลย (java file และ folder ก็จะ
ออกมายัง part ที่เราใช้คำสั่ง ก็ย้ายไปไว้ที่อื่นเอาเอง)
wsdl2java http://localhost:8080/project/services/cxfAuth?wsdl

ในที่นี้ได้เป็น part : com\company\auth\service\
AuthService.java
CorporateAuthService.java
Employee.java
GetEmployee.java
GetEmployeeResponse.java
ObjectFactory.java
package-info.java

java spring framework 3 quickstart

ในส่วนนี้จะเป็นการทบทวนเล็ก ๆ เนื่องจากห่างไปสักพัก กับ java spring framework
สำหรับผู้ที่ยังไม่มีประสพการณ์แนะนำอ่าน Servlet, JSP ก่อนเลยนะเพราะต่อยอดมาครับ

ขั้นแรกก็คงมิพ้น file
project/WebContent/WEB-INF/web.xml


    TestJsp
    
        contextConfigLocationclasspath:conf/applicationContext.xml
    
        org.springframework.web.context.ContextLoaderListener
    
    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocationclasspath:conf/dispatcher-servlet.xml
        2
    
    
        dispatcher
        *.do
    
    
        30
    
    
        redirect.jsp
    


ส่วนต่อมา ในการกำหนด การชี้ไปยัง page ต่าง ๆ ใช้ annotation เข้าไป scan หา
project/src/conf/dispatcher-servlet.xml


  
    

    
      
    

    
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    



ส่วนต่อมา ในการกำหนด service ในที่นี้เราใช้ annotation ทำให้ เขียน xml น้องลงไปเยอะ
project/src/conf/applicationContext.xml



    <context:annotation-config/>
    <context:component-scan base-package="spring.anno"/>



เริ่มแรกเรามาลอง service กันเลยดีกว่า เพื่อทดสอบ config
project/src/spring/anno/Iface.java
package spring.anno;

public interface Iface {
    public String getData(String str);
}

project/src/spring/anno/IfaceImpl.java
package spring.anno;

import org.springframework.stereotype.Service;

@Service
public class IfaceImpl implements Iface{

    @Override
    public String getData(String str) {
        System.out.println("have call IfaceImpl.getData() : " + str);
        return str;
    }
}

ต่อมาก็สร้างตัว test เลย กรณีนี้ใช้ eclipse เข้าช่วย ดังนั้น อาจต้องแก้ junit เป็น v4 ก่อน และ load ในส่วน library spring test เพิ่มมา
project/src/spring/anno/test/IfaceTest.java
package spring.anno.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 spring.anno.Iface;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/conf/applicationContext.xml"})
public class IfaceTest {
 
 private Iface iface;

 @Autowired
 public void setIface(Iface iface) {
  this.iface = iface;
 }

 @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");
  this.iface.getData("test 123 d^^");
 }
}

ถ้ามิมีสิ่งใดผิดพลาดจะเป็นประมาณนี้
6 พ.ค. 2553 9:18:56 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: @TestExecutionListeners is not present for class [class spring.anno.test.IfaceTest]: using defaults.
6 พ.ค. 2553 9:18:56 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [conf/applicationContext.xml]
6 พ.ค. 2553 9:18:57 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.GenericApplicationContext@fd13b5: startup date [Thu May 06 09:18:57 ICT 2010]; root of context hierarchy
6 พ.ค. 2553 9:18:57 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1b3f8f6: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,ifaceImpl,ifacePage]; root of factory hierarchy
setUp
testGetData
have call IfaceImpl.getData() : test 123 d^^
tearDown
6 พ.ค. 2553 9:18:57 org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.GenericApplicationContext@fd13b5: startup date [Thu May 06 09:18:57 ICT 2010]; root of context hierarchy
6 พ.ค. 2553 9:18:57 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1b3f8f6: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,ifaceImpl,ifacePage]; root of factory hierarchy

หลังจากนั้นเราก็มาเริ่มสร้าง controller กับเลยดีกว่า
project/src/spring/anno/web/IfacePage.java
package spring.anno.web;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import spring.anno.Iface;

@Controller
public class IfacePage {
    
    private Iface iface;
    
    @Autowired
    public void setIface(Iface iface) {
        this.iface = iface;
    }
    
    public Iface getIface() {
        return iface;
    }

    @RequestMapping("/helloWorld.do")
    public ModelMap helloWorld() {
        System.out.println("request get http for helloWorld...");
        return new ModelMap();
    }
    
    @RequestMapping("/helloCat.do")
    public ModelMap helloCat() {
        System.out.println("request get http for helloCat...");
        String data = this.getIface().getData("request get http for helloCat...");
        ModelMap mm = new ModelMap();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("data", data);
        List<Integer> l = new LinkedList<Integer>();
        for(int i=0; i<10; i++){
            l.add(i);
        }
        map.put("testloop", l);
        mm.addAllAttributes(map);
        return mm;
    }
}
และ template เพื่อแสดงผล ในหน้านี้ยังมิมีอะไร เอาง่าย ๆ ก่อน การเข้าถึง โดย
http://localhost:8080/proeject/helloWorld.do
project/WebContent/WEB-INF/jsp/helloWorld.jsp
<html>
    <head>
        <title>test helloWorld</title>
    </head>
    <body>
        data : helloWorld
    </body>
</html>
และ template เพื่อแสดงผล และทำการเีรียก service และ เริ่มผูกข้อมูลมายังหน้า page เอาง่าย ๆ อีกเช่นกัน การเข้าถึง โดย
http://localhost:8080/proeject/helloCat.do
project/WebContent/WEB-INF/jsp/helloCat.jsp
<html>
    <head>
        <title>test helloCat</title>
    </head>
    <body>
    data : helloCat
    <br />
    <% out.print(request.getAttribute("data")); %>
    <br />
    <table border="1">
    <%@page import="java.util.LinkedList"%>
    <%@page import="java.util.List"%>
    <%
    List<Integer> testloop = (List<Integer>)request.getAttribute("testloop");
    if( null == testloop){
        out.print("<tr><td>");
        out.print("data loop empty...");
        out.print("</td></tr>");
    }else{
        for(Integer i : testloop){
            out.print("<tr><td>");
            out.print(" test </td><td>" + i);
            out.print("</td></tr>");
        }
    }
%>
    </table>
    </body>
</html>
เพิ่มเติมในส่วน file redirect.jsp เพื่อเป็น default page site ซึ่งจะทำงานในกรณีที่เราใส่แค่
http://localhost:8080/proeject
(ซึ่งตอนนี้ยังคงไม่ได้ทำอะไรในตอนนี้ เอาไว้ต่อยอดในบทความถัด ๆ ไปครับ) project/WebContent/WEB-INF/redirect.jsp
<%--
Views should be stored under the WEB-INF folder so that
they are not accessible except through controller process.

This JSP is here to provide a redirect to the dispatcher
servlet but should be the only JSP outside of WEB-INF.
--%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<% response.sendRedirect("security/index.do"); %>

ข้อสำคัญอย่าลืม library ต่าง ๆ มาให้ครบ เพราะเราไม่ได้เขียนเองตั้งแต่ต้น เราพึ่งพา library ต่าง ๆ ของ spring และ อื่น ๆเป็นหลัก เขียนเองคงรอชาติเศษ ๆ หุหุ
ไม่แน่ใจว่าตกหล่นอะไรไปหรือเปล่า เพราะทำแล้วลองแล้ว ก็ลากมาแปะไว้ในนี้เลย ^^'
จับผิดได้แจ้งด้วยนะครับ
ขอบคุณครับ