Java

【 BlazeDS + SpringFramework + MyBatis + HSQLDBの連携 】



Spring Framework3.0.5 - DI(Dependency Injection)コンテナ
MyBatis3.0.6 - O/R Mapper
HSQLDB2.2.5 - Java製RDB



Spring2.0.x + iBatis2.x + HSQLDB1.8.xを Spring3.0.5 + MyBatis3.0.6 + HSQLDB2.2.5で置き換えてみました。
こちらはAnnotationベースでの実装となります。

DBのScirpt, FlexのMXML・ASは変わりませんので、XMLベースで実装したドキュメントを参考にして下さい。
MyBatisを使用して、Annotationベースの実装を2種類(Annotation指定のものと、 SelectBuilder/SqlBuilderを使用したもの)試してみました。
XML書かなくていいので、こちらの方が便利ではあるけれど、ソース追いかけるのがヤヤコシイかな?



[ web.xml ]
---
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Spring BlazeDS</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>


    <!-- Filter config with GZIP -->
    <filter>
        <filter-name>CompressingFilter</filter-name>
        <filter-class>
            com.planetj.servlet.filter.compression.CompressingFilter
        </filter-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>statsEnabled</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CompressingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
   
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
   
    <servlet>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
        <init-param>
            <param-name>services.configuration.file</param-name>
            <param-value>/WEB-INF/flex/services-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>HttpContextUtilServlet</servlet-name>
        <servlet-class>com.chocbanana.http.HttpContextUtilServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>


    <servlet-mapping>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>
</web-app>
[ EOF ]
---

[ HttpContextUtilServlet.java ]
---
package com.chocbanana.http;

import java.io.Serializable;

import javax.servlet.ServletException;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;


public class HttpContextUtilServlet extends javax.servlet.http.HttpServlet implements Serializable {

    private static WebApplicationContext ctx;
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        try{
            //dao.shutdown();
        }catch(Exception e){
            e.printStackTrace();
        }
        super.destroy();
    }

    @Override
    public synchronized void init() throws ServletException {
        // TODO Auto-generated method stub
        super.init();
        try{
            if(ctx == null) ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public synchronized static WebApplicationContext getContext(){
        return ctx;
    }
}
[ EOF ]
---



[ applicationContext.xml ]
---
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- jdbc.propertiesを認識させる propertyConfigurer -->
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>
                    classpath:/com/chocbanana/dao/database.properties
                </value>
            </list>
        </property>
    </bean>

    <!-- jdbc.propertiesの設定値をdataSourceにインジェクション -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.url}" />
        <property name="username" value="${db.user}" />
        <property name="password" value="${db.pass}" />
    </bean>

    <!-- Dao Mapperのサービスクラス・パッケージをコンポーネントスキャンだぜ! -->
    <context:component-scan base-package="com.chocbanana.dao.service"/>
     
    <!-- enable autowire -->
    <context:annotation-config />
   
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>
   
    <!-- トランザクション制御のインターセプターを構成 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
   
    <!-- 実装クラスのTransactionalアノテーションでジョイントポイントの認識をさせる  -->
    <!-- @transaction-managerのデフォルトは"transactionManager"だが念のため指定  -->
    <!-- @proxy-target-classをtureにしておく。CGLIBでプロキシーを作る方が多少速いらしい -->
    <!-- -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
   

    <!-- アノテーションベースのMapper -->
    <!--
     - コンポーネントスキャンでスキャンだぜ。Injectionされるだぜー。
     - MyBatisのDAO Mapperです。
    -->
     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.chocbanana.dao.mapper" />
    </bean>
</beans>
[ EOF ]
---


[ database.properties ]
---
#Wed Apr 01 22:43:23 JST 2009
db.pass=
#db.url=jdbc\:hsqldb\:file:/Users/hoehoe/Documents/workspace/WEB-INF/resource/db
db.url=jdbc\:hsqldb\:file:C:/Documents and Settings/hoehoe/My Documents/EclipseProject/SpringBlazeDS/WEB-INF/resource/db
db.driver=org.hsqldb.jdbc.JDBCDriver
db.user=sa
[ EOF ]
---


[ DaoMapper.java ]
---
package com.chocbanana.dao.mapper;

import java.util.ArrayList;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectProvider;
import org.springframework.dao.DataAccessException;

import com.chocbanana.bean.PostMessage;

public interface DaoMapper {

    /* Annotation base */
    @Select("SELECT TOP 1000 * FROM MESSAGE ORDER BY ID DESC")
    ArrayList<PostMessage> getMessages() throws DataAccessException;

    @Insert("INSERT INTO MESSAGE (NAME, MAIL, MESSAGE, DAY) VALUES (#{name}, #{mail}, #{message}, #{day})")
    int putMessages(PostMessage Message) throws DataAccessException;
   
    /* SelectBuilder and SqlBuilder base  */
    @SelectProvider(type=com.chocbanana.dao.provider.SelectProvider.class, method="selectMessage")
    ArrayList<PostMessage> selectMessages() throws DataAccessException;
   
    @SelectProvider(type=com.chocbanana.dao.provider.SelectProvider.class, method="selectOne")
    PostMessage selectOne(PostMessage message) throws DataAccessException;

    @InsertProvider(type=com.chocbanana.dao.provider.SqlProvider.class, method="insertMessage")
    int insertMessages(PostMessage messge) throws DataAccessException;
   
}
[ EOF ]
---

[ SelectProvider.java ]
---
package com.chocbanana.dao.provider;

import static org.apache.ibatis.jdbc.SelectBuilder.BEGIN;
import static org.apache.ibatis.jdbc.SelectBuilder.FROM;
import static org.apache.ibatis.jdbc.SelectBuilder.SELECT;
import static org.apache.ibatis.jdbc.SelectBuilder.SQL;
import static org.apache.ibatis.jdbc.SelectBuilder.ORDER_BY;
import static org.apache.ibatis.jdbc.SelectBuilder.WHERE;

import com.chocbanana.bean.PostMessage;

public class SelectProvider {
     public static String selectMessage() {
         BEGIN();
         SELECT("TOP 1000 * ");
         FROM("MESSAGE");
         ORDER_BY("ID DESC");
         return SQL();
     }

     public static String selectOne(PostMessage message) {
         BEGIN();
         SELECT("*");
         FROM("MESSAGE");
         WHERE("ID like #{id}");
         return SQL();
     }
}
[ EOF ]
---


[ SqlProvider.java ]
---
package com.chocbanana.dao.provider;

import static org.apache.ibatis.jdbc.SelectBuilder.BEGIN;
import static org.apache.ibatis.jdbc.SqlBuilder.INSERT_INTO;
import static org.apache.ibatis.jdbc.SqlBuilder.VALUES;
import static org.apache.ibatis.jdbc.SqlBuilder.SQL;

import com.chocbanana.bean.PostMessage;

public class SqlProvider {
     public static String insertMessage(PostMessage message){
         BEGIN();
         INSERT_INTO("MESSAGE");
         VALUES("NAME, MAIL, MESSAGE, DAY", "#{name}, #{mail}, #{message}, #{day}");
         return SQL();
     }
}
[ EOF ]
---


[ FooService.java ]
---
package com.chocbanana.dao.service;

import java.util.ArrayList;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.annotation.Transactional;

import com.chocbanana.bean.PostMessage;

@Transactional
public interface FooService {
    public ArrayList<PostMessage> getMessages() throws DataAccessException;
    public int putMessages(PostMessage Message) throws DataAccessException;
    public ArrayList<PostMessage> selectMessages() throws DataAccessException;
    public PostMessage selectOne(PostMessage message) throws DataAccessException;
    public int insertMessages(PostMessage messge) throws DataAccessException;
}

[ EOF ]
---

[ FooServiceImpl.java ]
---
package com.chocbanana.dao.service;

import java.util.ArrayList;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import com.chocbanana.bean.PostMessage;
import com.chocbanana.dao.mapper.DaoMapper;

@Service("fooService")
public class FooServiceImpl implements FooService {
   
    @Autowired
    private DaoMapper daoMapper;
   
    public void setDaoMapper(DaoMapper daoMapper){
        this.daoMapper = daoMapper;
    }
   
    public ArrayList<PostMessage> getMessages() throws DataAccessException{
        return this.daoMapper.getMessages();
    }
    public int putMessages(PostMessage Message) throws DataAccessException{
        return this.daoMapper.putMessages(Message);       
    }
    public ArrayList<PostMessage> selectMessages() throws DataAccessException{
        return this.daoMapper.selectMessages();
    }
    public PostMessage selectOne(PostMessage message) throws DataAccessException{
        return this.daoMapper.selectOne(message);
    }
    public int insertMessages(PostMessage messge) throws DataAccessException{
        return this.daoMapper.insertMessages(messge);
    }
}
[ EOF ]
---



[ FlexService.java ]
---
package com.chocbanana.ws;

import java.text.SimpleDateFormat;
import java.util.ArrayList;

import javax.annotation.Resource;

import com.chocbanana.bean.*;
import com.chocbanana.dao.Db2DaoInterface;
import com.chocbanana.dao.service.FooService;
import com.chocbanana.http.HttpContextUtilServlet;

@SuppressWarnings("serial")
public class FlexService implements java.io.Serializable{

    @Resource
    private static FooService service;

    private static final String BEAN_TYPE="fooService";

    public FlexService(){
        service = (FooService) HttpContextUtilServlet.getContext().getBean(BEAN_TYPE);
    }
    public ArrayList<ResultMessage> getMessages(){
       
        ArrayList<ResultMessage> result = new ArrayList<ResultMessage>();
        try{
            ArrayList<PostMessage> list;
            list = service.selectMessages(); /* service.getMessages(); */

            java.util.Iterator<PostMessage> it = list.iterator();
            ResultMessage resultMsg = null;

            while(it.hasNext()){
                PostMessage _msg = it.next();
                resultMsg = new ResultMessage();
                resultMsg.setMessage(
                        _msg.getId() +  " " +
                        _msg.getName() + " " +
                        _msg.getMail() + " " +
                        _msg.getDay() + " " + "\n\n" +
                        _msg.getMessage());
                result.add(resultMsg);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return result;
       
    }
    public int putMessages(PostMessage message) {
        int ret=0;
        String day = "";
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        day = format.format(new java.util.Date());
        message.setDay(day);
       
        if("".equals(message.getMessage())){
            return 0;
        }
       
        try{
            ret = service.insertMessages(message); /* service.putMessages(message); */
        }catch(Exception e){
            e.printStackTrace();
        }
            return ret;
    }
}
[ EOF ]
---

【 BlazeDS + SpringFramework + MyBatis + HSQLDBの連携 】



Spring Framework3.0.5 - DI(Dependency Injection)コンテナ
MyBatis3.0.6 - O/R Mapper
HSQLDB2.2.5 - Java製RDB



Spring2.0.x + iBatis2.x + HSQLDB1.8.xを Spring3.0.5 + MyBatis3.0.6 + HSQLDB2.2.5で置き換えてみました。
こちらはXMLベースでの実装となります。

packageの階層に注意してください。



[ db.script -抜粋- ]
---
CREATE CACHED TABLE MESSAGE(
    ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY,
    NAME VARCHAR(255) NOT NULL,
    MAIL VARCHAR(255) NOT NULL,
    MESSAGE VARCHAR(65535) NOT NULL,
    DAY VARCHAR(255) NOT NULL
    );
[ EOF ]
---



[ web.xml ]
---
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Spring BlazeDS</display-name>

    <!-- Contextに渡すXML -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext-xml.xml</param-value>
    </context-param>


    <!-- Filter config with GZIP -->
    <filter>
        <filter-name>CompressingFilter</filter-name>
        <filter-class>
            com.planetj.servlet.filter.compression.CompressingFilter
        </filter-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>statsEnabled</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CompressingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
   
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
   
    <servlet>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
        <init-param>
            <param-name>services.configuration.file</param-name>
            <param-value>/WEB-INF/flex/services-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>HttpContextUtilServlet</servlet-name>
        <servlet-class>com.chocbanana.http.HttpContextUtilServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>


    <servlet-mapping>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>
</web-app>
[EOF]
---


[ HttpContextUtilServlet.java ]
---
package com.chocbanana.http;

import java.io.Serializable;

import javax.servlet.ServletException;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;


public class HttpContextUtilServlet extends javax.servlet.http.HttpServlet implements Serializable {

    private static WebApplicationContext ctx;
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        try{
            //dao.shutdown();
        }catch(Exception e){
            e.printStackTrace();
        }
        super.destroy();
    }

    @Override
    public synchronized void init() throws ServletException {
        // TODO Auto-generated method stub
        super.init();
        try{
            if(ctx == null) ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public synchronized static WebApplicationContext getContext(){
        return ctx;
    }
}
[EOF]
---


[ applicationContext.xml ]
---
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/tx
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <!-- jdbc.propertiesを認識させる propertyConfigurer -->
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>
                    classpath:/com/chocbanana/dao/database.properties
                </value>
            </list>
        </property>
    </bean>

    <!-- jdbc.propertiesの設定値をdataSourceにインジェクション -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${db.driver}" />
        <property name="url" value="${db.url}" />
        <property name="username" value="${db.user}" />
        <property name="password" value="${db.pass}" />
    </bean>

    <!-- MyBatisのフロントになるsqmMapClientを構成。プロパティ値にMapper.xmlのパスを指定するだけ -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:/com/chocbanana/dao/Mapper.xml" />
    </bean>
   
    <!-- トランザクション制御のインターセプターを構成 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
   
    <!-- 実装クラスのTransactionalアノテーションでジョイントポイントの認識をさせる  -->
    <!-- @transaction-managerのデフォルトは"transactionManager"だが念のため指定  -->
    <!-- @proxy-target-classをtureにしておく。CGLIBでプロキシーを作る方が多少速いらしい -->
    <!-- -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
   
    <!-- XMLベースのDAO -->
    <bean id="daoXml" class="com.chocbanana.dao.Db2Dao">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    </bean>
</beans>
[EOF]
---

[ database.properties ]
---
#Wed Apr 01 22:43:23 JST 2009
db.pass=
#db.url=jdbc\:hsqldb\:file:/Users/hoehoe/Documents/workspace/WEB-INF/resource/db
db.url=jdbc\:hsqldb\:file:C:/Documents and Settings/hoehoe/My Documents/EclipseProject/SpringBlazeDS/WEB-INF/resource/db
db.driver=org.hsqldb.jdbc.JDBCDriver
db.user=sa
[EOF]
---




[ Db2DaoInterface.java ]
---
/*
 * Created on 2005/06/19
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.chocbanana.dao;

import java.util.ArrayList;

import org.springframework.dao.DataAccessException;
import com.chocbanana.bean.PostMessage;
/**
 * @author hoehoe
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public interface Db2DaoInterface {
   
    ArrayList<PostMessage> getMessages() throws DataAccessException;
    int putMessages(PostMessage Message) throws DataAccessException;
   
    void shutdown() throws DataAccessException;
   
}
[EOF]
---

[ Db2Dao.java ]
---
/*
 * Created on 2005/06/18
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.chocbanana.dao;

import java.sql.SQLException;
import java.util.ArrayList;

import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.chocbanana.bean.PostMessage;

/**
 * @author hoehoe
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class Db2Dao extends SqlSessionDaoSupport implements Db2DaoInterface{
    @SuppressWarnings("unchecked")
    @Transactional(readOnly=true, propagation=Propagation.REQUIRED)
    public ArrayList<PostMessage> getMessages() throws DataAccessException{
        return (ArrayList<PostMessage>) getSqlSession().selectList("com.chocbanana.dao.Mapper.getMessage");
    }
   
    @Transactional(readOnly=false, rollbackFor=SQLException.class)
    public int putMessages(PostMessage message) throws DataAccessException{
        return getSqlSession().insert("com.chocbanana.dao.Mapper.putMessage", message);
    }

    public void shutdown() throws DataAccessException{
        getSqlSession().selectOne("shutdown");
    }
}
[EOF]
---


[ /com/chocbanana/dao/Mapper.xml ]
---
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
    Copyright 2010-2011 The myBatis Team

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->

<!--
    version: $Id: UserMapper.xml 2444 2010-09-15 07:38:37Z simone.tripodi $
-->
<mapper namespace="com.chocbanana.dao.Mapper">
    <select id="getMessage"  resultType="com.chocbanana.bean.PostMessage">
        SELECT TOP 1000 * FROM MESSAGE ORDER BY ID DESC
    </select>

    <insert id="putMessage" parameterType="com.chocbanana.bean.PostMessage" >
        INSERT INTO MESSAGE (NAME, MAIL, MESSAGE, DAY) VALUES (#{name}, #{mail}, #{message}, #{day})
    </insert>

    <!-- database shutdown -->
    <sql id="shutdown">shutdown</sql>
</mapper>
[EOF]
---



[ FlexService.java ]
---
package com.chocbanana.ws;

import java.text.SimpleDateFormat;
import java.util.ArrayList;

import com.chocbanana.bean.*;
import com.chocbanana.dao.Db2DaoInterface;
import com.chocbanana.http.HttpContextUtilServlet;

@SuppressWarnings("serial")
public class FlexService implements java.io.Serializable{
   
    private static Db2DaoInterface dao;
    private static final String BEAN_TYPE = "daoXml";

    public FlexService(){
        dao = (Db2DaoInterface) HttpContextUtilServlet.getContext().getBean(BEAN_TYPE);
    }
    public ArrayList<ResultMessage> getMessages(){
       
        ArrayList<ResultMessage> result = new ArrayList<ResultMessage>();
        try{
            ArrayList<PostMessage> list = dao.getMessages();
            java.util.Iterator<PostMessage> it = list.iterator();
            ResultMessage resultMsg = null;

            while(it.hasNext()){
                PostMessage _msg = it.next();
                resultMsg = new ResultMessage();
                resultMsg.setMessage(
                        _msg.getId() +  " " +
                        _msg.getName() + " " +
                        _msg.getMail() + " " +
                        _msg.getDay() + " " + "\n\n" +
                        _msg.getMessage());
                result.add(resultMsg);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return result;
       
    }
    public int putMessages(PostMessage message) {
        int ret=0;
        String day = "";
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        day = format.format(new java.util.Date());
        message.setDay(day);
       
        if("".equals(message.getMessage())){
            return 0;
        }
       
        try{
            ret = dao.putMessages(message);
        }catch(Exception e){
            e.printStackTrace();
        }
            return ret;
    }
}
---


- Flexのソースコード一覧 -

[ SpringBlazeDS.mxml ]
---
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               minWidth="950" minHeight="500" viewSourceURL="srcview/index.html">

    <fx:Script source="SpringBlazeDS.as"/>
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
        <s:RemoteObject id="srv" endpoint="{'http://{server.name}:{server.port}/SpringBlazeDS/messagebroker/amf'}" destination="flexService" showBusyCursor="true">
            <s:method name="getMessages" result="resultMessage(event)" fault="faultMessage(event)"/>
        </s:RemoteObject>
    </fx:Declarations>

    <s:Panel x="0" y="0" width="100%" height="100%" title="SpringBlazeDS">
        <s:HGroup x="10" y="10" width="100%" verticalAlign="middle">
            <s:Spacer width="20" height="10"/>
            <s:Button id="btn_post" label="Post" click="btn_post_clickHandler(event)"
                      fontWeight="bold"/>
            <s:Spacer width="15" height="10"/>
            <s:Button id="btn_reload" label="Reload" click="btn_reload_clickHandler(event)"
                      fontWeight="bold"/>
        </s:HGroup>
        <s:Spacer x="10" y="39" width="20" height="10"/>
        <s:HGroup x="10" y="56" width="100%" height="85%">
            <s:DataGrid width="100%" height="100%" dataProvider="{srv.getMessages.lastResult}" creationComplete="d1_creationCompleteHandler(event)">
                <s:columns>
                    <s:ArrayList>
                        <s:GridColumn dataField="message" headerText="" />
                    </s:ArrayList>
                </s:columns>
            </s:DataGrid>
            <s:Spacer width="10" height="10"/>
        </s:HGroup>
       
    </s:Panel>
</s:Application>
[ EOF ]
---


[ SpringBlazeDS.as ]
---

import flash.events.MouseEvent;

import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.managers.PopUpManager;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

protected function btn_post_clickHandler(event:MouseEvent):void
{
    // TODO Auto-generated method stub
    var post:Post = Post(PopUpManager.createPopUp(this, Post, true));
    post.addEventListener(FlexEvent.REMOVE, closePopUp);
    PopUpManager.centerPopUp(post);
}
private function closePopUp(e:FlexEvent):void{
    srv.getMessages();
}

public function resultUser(event:ResultEvent):void{

}
protected function btn_reload_clickHandler(event:MouseEvent):void
{
    // TODO Auto-generated method stub
    srv.getMessages();
}

protected function d1_creationCompleteHandler(event:FlexEvent):void
{
    // TODO Auto-generated method stub
    srv.getMessages();
}

public function resultMessage(event:ResultEvent):void{

}

public function faultMessage(event:FaultEvent):void{
    Alert.show(event.toString());
}
[ EOF ]
---


[ Post.mxml ]
---
<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               width="800" height="400" creationComplete="t1_creationCompleteHandler(event)" close="t1_closeHandler(event)" title="PostMessage">
   
    <fx:Script>
        <![CDATA[
            import com.chocbanana.bean.PostMessge;
           
            import mx.controls.Alert;
            import mx.events.CloseEvent;
            import mx.events.FlexEvent;
            import mx.managers.PopUpManager;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
           
            protected function btn_cancel_clickHandler(event:MouseEvent):void
            {
                // TODO Auto-generated method stub
                txt_message.text = "";
            }
           
            protected function btn_post_clickHandler(event:MouseEvent):void
            {
                // TODO Auto-generated method stub
                var post:PostMessge = new PostMessge();
                post.name = txt_name.text;
                if(post.name == ""){
                    post.name = "以下、名無しに変わりましてfxug-nagoyaがお送りします";
                }
                post.mail = "sage";
                post.message = txt_message.text;
                if(post.message == ""){
                    Alert.show("本文が空です。");
                    return;
                }
                srv.putMessages(post);
            }
           
            protected function t1_closeHandler(event:CloseEvent):void
            {
                // TODO Auto-generated method stub
                PopUpManager.removePopUp(this);
            }
           
            protected function t1_creationCompleteHandler(event:FlexEvent):void
            {
                // TODO Auto-generated method stub
                txt_message.setFocus();
            }
           
            protected function method1_resultHandler(event:ResultEvent):void
            {
                // TODO Auto-generated method stub
                PopUpManager.removePopUp(this);
               
            }
           
            protected function method1_faultHandler(event:FaultEvent):void
            {
                // TODO Auto-generated method stub
                Alert.show(event.message.toString());
            }
           
        ]]>
    </fx:Script>
   
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
        <s:RemoteObject id="srv" endpoint="{'http://{server.name}:{server.port}/SpringBlazeDS/messagebroker/amf'}" destination="flexService" showBusyCursor="true">
            <s:method name="putMessages" result="method1_resultHandler(event)" fault="method1_faultHandler(event)" />
        </s:RemoteObject>
    </fx:Declarations>
    <s:VGroup x="10" y="10" width="780" height="346">
        <s:HGroup verticalAlign="middle">
            <s:Label text="Name: "/>
            <s:TextInput id="txt_name" width="320" text="以下、名無しに変わりましてfxug-nagoyaがお送りします"/>
        </s:HGroup>
        <s:VGroup width="770" verticalAlign="bottom">
            <s:Spacer height="5" width="5" />
            <s:Label text="Message: "/>
            <s:TextArea id="txt_message" width="100%" height="200"/>
        </s:VGroup>
        <s:Spacer width="20" height="10"/>
        <s:HGroup width="100%" horizontalAlign="right" verticalAlign="middle">
            <s:Button id="btn_cancel" label="Cancel" click="btn_cancel_clickHandler(event)"/>
            <s:Spacer width="20" height="10"/>
            <s:Button id="btn_post" label="Post" click="btn_post_clickHandler(event)"
                      fontWeight="bold"/>
            <s:Spacer width="20" height="10"/>
        </s:HGroup>
    </s:VGroup>

</s:TitleWindow>

[ EOF ]
---



社内から帳票作成とその情報を一括管理出来るように…という要望を頂いたので、AdobeAIRで実装してみました。


サンプルアプリのソースFxUG名古屋勉強会資料_20110709.pdf を置いておきます。


必要となるJavaのライブラリ一式。
----------------------
aopalliance-1.0.jar
asm-2.2.3.jar
backport-util-concurrent.jar
cglib-nodep-2.1_3.jar
commons-beanutils.jar
commons-collections-3.2.1.jar
commons-dbcp-1.4.jar
commons-digester.jar
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging-1.1.1.jar
commons-pool-1.5.4.jar
cxf-2.2.12.jar
FastInfoset-1.2.7.jar
flex-messaging-common.jar
flex-messaging-core.jar
flex-messaging-remoting.jar
freemarker-2.3.16.jar
geronimo-annotation_1.0_spec-1.1.1.jar
geronimo-servlet_2.5_spec-1.2.jar
geronimo-stax-api_1.0_spec-1.0.1.jar
geronimo-ws-metadata_2.0_spec-1.1.2.jar
ibatis-2.3.4.726.jar
iText-2.1.7.jar
iTextAsian.jar
jasperreports-4.0.1.jar
jasperreports-extensions-3.5.3.jar
jasperreports-fonts-4.0.1.jar
javassist.jar
jaxb-api-2.1.jar
jaxb-impl-2.1.13.jar
jcommon-1.0.16.jar
jdt-compiler-3.1.1.jar
log4j-1.2.16.jar
log4j.dtd
neethi-2.0.4.jar
ognl-3.0.jar
opensaml-1.1.jar
pjl-comp-filter-1.7.jar
poi-3.7-20101029.jar
postgresql-8.3-603.jdbc4.jar
saaj-api-1.3.jar
saaj-impl-1.3.2.jar
serializer-2.7.1.jar
spring-aop.jar
spring-beans.jar
spring-context-support.jar
spring-context.jar
spring-core.jar
spring-jdbc.jar
spring-jms.jar
spring-orm.jar
spring-test.jar
spring-tx.jar
spring-web.jar
spring-webmvc-struts.jar
spring.jar
sql-map-2.dtd
sql-map-config-2.dtd
struts2-convention-plugin-2.2.1.jar
struts2-core-2.2.1.jar
wsdl4j-1.6.2.jar
wss4j-1.5.8.jar
wstx-asl-3.2.9.jar
xalan-2.7.1.jar
xml-resolver-1.2.jar
XmlSchema-1.4.5.jar
xmlsec-1.4.3.jar
xwork-core-2.2.1.jar
----------------------



PDFとして作成された帳票を表示。
pict0003.jpg


iReportでテンプレートを作成し、データを流し込んで、PDFを作成します。

pict0005.jpg


※データは架空のものです。
ご参考まで。

ApacheとResinとStruts2とFlex(BlazeDS)の連携

メモしときます。

Project名はHello。
HelloWorld ! を表示します。


[ resin.conf ]
----------
# web-appのRoot設定
<web-app id="/" root-directory='C:\Documents and Settings\hoehoe\My Documents\EclipseProject\Hello'/>
----------


[ WEB-INF/lib/ ]
----------
aopalliance-1.0.jar
asm-2.2.3.jar
backport-util-concurrent.jar
cglib-nodep-2.1_3.jar
commons-beanutils.jar
commons-dbcp-1.4.jar
commons-digester.jar
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging-1.1.1.jar
commons-pool-1.5.4.jar
cxf-2.2.9.jar
dir.txt
FastInfoset-1.2.7.jar
flex-messaging-common.jar
flex-messaging-core.jar
flex-messaging-remoting.jar
freemarker-2.3.16.jar
geronimo-annotation_1.0_spec-1.1.1.jar
geronimo-servlet_2.5_spec-1.2.jar
geronimo-stax-api_1.0_spec-1.0.1.jar
geronimo-ws-metadata_2.0_spec-1.1.2.jar
ibatis-2.3.4.726.jar
javassist.jar
jaxb-api-2.1.jar
jaxb-impl-2.1.13.jar
jcommon-1.0.16.jar
log4j-1.2.16.jar
log4j.dtd
neethi-2.0.4.jar
ognl-3.0.jar
opensaml-1.1.jar
pjl-comp-filter-1.7.jar
postgresql-8.3-603.jdbc4.jar
saaj-api-1.3.jar
saaj-impl-1.3.2.jar
serializer-2.7.1.jar
spring-aop.jar
spring-beans.jar
spring-context-support.jar
spring-context.jar
spring-core.jar
spring-jdbc.jar
spring-jms.jar
spring-orm.jar
spring-test.jar
spring-tx.jar
spring-web.jar
sql-map-2.dtd
sql-map-config-2.dtd
struts2-convention-plugin-2.2.1.jar
struts2-core-2.2.1.jar
wsdl4j-1.6.2.jar
wss4j-1.5.8.jar
wstx-asl-3.2.9.jar
xalan-2.7.1.jar
xml-resolver-1.2.jar
XmlSchema-1.4.5.jar
xmlsec-1.4.3.jar
xwork-core-2.2.1.jar
----------




[ web.xml ]
----------
<?xml version="1.0"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <!-- Flex(BlazeDSもFilter経由。先にBlazeDSのFilterを記述しておくこと) -->
    <filter>
        <filter-name>flex</filter-name>
        <filter-class>hello.filter.FlexFilter</filter-class>
        <init-param>
            <param-name>services.configuration.file</param-name>
            <param-value>/WEB-INF/flex/services-config.xml</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>flex</filter-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </filter-mapping>


    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
        </filter-class>
        <init-param>
            <param-name>actionPackages</param-name>
            <param-value>hello.action</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/struts2/*</url-pattern>
    </filter-mapping>
</web-app>
[EOF]
----------


Struts2の配置 アノテーションを使用するのでstruts.xmlなどは無いです。

[ hello.action.HelloAction ]
----------
package hello.action;

import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;

import com.opensymphony.xwork2.ActionSupport;

/**
 * 相変わらず標準出力に欧米の挨拶をかますだけのアクション。
 */
@Namespace("/struts2")
@Results({
    @Result(name="success", location="hello.jsp"),
    @Result(name="failure", location="error.jsp")
})
public class HelloAction extends ActionSupport {
//public class SayHelloAction {
    private static final long serialVersionUID = 1L;

    /**
     * アクション実行メソッド。
     */
    public String execute() {
        // アクション実行の結果を文字列で返す。
        return ActionSupport.SUCCESS;
    }
    public String getMessage(){
        return "Hello World!!";
    }
}
[ EOF ]
----------


[ /WEB-INF/content/struts2/hello.jsp ]
----------
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:property value="message"/>
</body>
</html>
[ EOF ]
----------


[ hello.filter.FlexFilter ]
----------
package hello.filter;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.mock.web.MockServletConfig;

import flex.messaging.MessageBrokerServlet;

/**
 * The MessageBrokerServlet has a lower priority than Filters, regardless of the
 * url-pattern we use. For that reason I created a filter that directly extends
 * the MessageBrokerServlet but behaves as a filter. This might be quite dodgy
 * but that's the only way I found to integrate struts 2 and Adobe Flex/BlazeDS.
 * Anyone who knows a simpler way please email me at info at spltech.co.uk
 *
 * @author Armindo Cachada
 *
 */
public class FlexFilter extends MessageBrokerServlet implements Filter {

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    public void destroy() {
        super.destroy();
    }

    /**
     * If this method is called the parent service method of the
     * MessageBrokerServlet is called, which does whatever BlazeDS needs to do
     * to communicate with the flex client. Note Here that any subsequent filter
     * will not be called because I am not invoking filterChain.doFilter/ That
     * is on purpose, because if it does, the normal struts 2 action mapping
     * mechanism will be called.
     *
     */
    public void doFilter(ServletRequest servletrequest,
            ServletResponse servletresponse, FilterChain filterchain)
            throws IOException, ServletException {
        this.service((HttpServletRequest) servletrequest,
                (HttpServletResponse) servletresponse);
    }

    /**
     * Note the use here of MockServletConfig. This utility class is available
     * in the spring framework. It is meant to be used for testing but I am
     * actually giving it a real purpose :)
     */
    public void init(FilterConfig filterconfig) throws ServletException {
        System.out.println("filter called");
        MockServletConfig servletConfig = new MockServletConfig(filterconfig
                .getServletContext());
        Enumeration filterParameters = filterconfig.getInitParameterNames();

        while (filterParameters.hasMoreElements()) {
            String filterParameter = (String) filterParameters.nextElement();
            System.out.println("Found parameter: " + filterParameter);
            String value = filterconfig.getInitParameter(filterParameter);
            servletConfig.addInitParameter(filterParameter, value);

        }
        super.init(servletConfig);
    }

}
[ EOF ]
----------


[ /WEB-INF/flex/services-config.xml ]
----------
<?xml version="1.0" encoding="UTF-8"?>
<services-config>

    <services>
        <!-- service要素には何でもいいからIDが要る模様 -->
        <service id="remoting-service"
            class="flex.messaging.services.RemotingService">
            <adapters>
                <adapter-definition id="java-object"
                    class="flex.messaging.services.remoting.adapters.JavaAdapter"
                    default="true" />
            </adapters>
            <default-channels>
                <channel ref="my-amf" />
                <channel ref="my-secure-amf"/>
            </default-channels>

            <!-- destination要素のIDが mx:RemoteObjectの destination プロパティに対応する -->
            <!-- リモート呼び出しを可能にしたい Beanの数だけ destination要素を記述する -->
            <destination id="fxService">
                <properties>
                    <!-- リモート呼び出ししたいJava Beansのクラス名 -->
                    <source>hello.ws.FlexService</source>
                    <!-- 注:この Beanはステートレスである -->
                </properties>
            </destination>
        </service>
    </services>

    <channels>
        <channel-definition id="my-amf"
            class="mx.messaging.channels.AMFChannel">
            <!--
                ここで構成されたエンドポイントURLを mx:RemoteObjectの
                endpoint プロパティにセットする
                {}内は実行時に自動で置き換えられるのでこの記述のままで良い。
            -->
            <endpoint
                url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
                class="flex.messaging.endpoints.AMFEndpoint" />
            <properties>
                <add-no-cache-headers>false</add-no-cache-headers>
            </properties>
        </channel-definition>

        <!-- SSL用チャンネルの定義 -->
        <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
            <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/>
            <properties>
                <add-no-cache-headers>false</add-no-cache-headers>
            </properties>
        </channel-definition>
    </channels>
</services-config>
[ EOF ]
----------


[ hello.ws.FlexService ]
----------
package hello.ws;

@SuppressWarnings("serial")
public class FlexService implements java.io.Serializable{
        
    public FlexService(){
    }
    public String sayHello(){
        return "Hello World !";
    }
}
[ EOF ]
----------



Apache
[ httpd.conf -Apache- ]
----------
#
# mod_caucho Resin Configuration
#
#Include conf/extra/httpd-proxy.conf
LoadModule caucho_module /usr/local/apache/modules/mod_caucho.so

ResinConfigServer 127.0.0.1 6800
CauchoConfigCacheDirectory /tmp
CauchoStatus yes
<Location /struts2/*>
  SetHandler caucho-request
</Location>
[ EOF ]
----------


Flex
[ Hello.mxml ]
----------
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" backgroundColor="white" creationComplete="init();">
<mx:Script source="Hello.as"/>
    <mx:RemoteObject id="srv" endpoint="{lbl_endPoint.text}" destination="fxService" showBusyCursor="true">
        <mx:method name="sayHello" result="resultHello(event);" fault="faultHello(event);"/>
    </mx:RemoteObject>
    <mx:Button x="65" y="59" label="click" id="btn_message" click="btn_message_click();"/>
    <mx:Text x="38" y="33" id="txt_message" text="{srv.sayHello.lastResult}"/>
    <mx:Label x="84" y="114" id="lbl_endPoint"/>
</mx:Application>
[ EOF ]
----------

[ Hello.as ]
----------
// ActionScript file
import hello.EndPoint;

import mx.controls.Alert;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

private function init():void{
    lbl_endPoint.text = EndPoint.getEndPointRevers();
}
private function btn_message_click():void{
    srv.sayHello();
}

private function resultHello(event:ResultEvent):void{
    Alert.show(event.message.toString());
}
private function faultHello(event:FaultEvent):void{
    Alert.show(event.message.toString());
}
[ EOF ]
----------


[ hello.EndPoint.as ]
----------
package hello {

    import flash.external.ExternalInterface;
   
    import mx.messaging.ChannelSet;
    import mx.messaging.channels.AMFChannel;
    import mx.rpc.remoting.mxml.RemoteObject;

    public class EndPoint {
       
        private static var remote:RemoteObject;
       
        public function EndPoint() {
            //TODO: implement function
        }
        public static function getEndPointRevers():String{
            var endPointURL:String = ExternalInterface.call("getFQDN");
            endPointURL = endPointURL + "messagebroker/amf";
            return endPointURL;
        }
        public static function getRemoteObject():RemoteObject{
            //var remote:RemoteObject = new RemoteObject();
            if(remote == null){
                remote = new RemoteObject();
                var cs:ChannelSet = new ChannelSet();
                var ac:AMFChannel = new AMFChannel("my-amf", EndPoint.getEndPointRevers());
                cs.addChannel(ac);
                remote.destination = "flexService";
                remote.channelSet = cs;
            }
            return remote;
        }
    }
}
[ EOF ]
----------

[ index.template.html(JavaScript) ]
----------
<script language="JavaScript" type="text/javascript">
<!--
//------------------------------------------------------------------------------
// MyScript
function setTitle(){
    if(location.href.indexOf("#",0) == -1){
        document.title = "IntraMaster";
    }else{
        document.title = "IntraMaster";
    }
}
function getURL(){
    return (document.URL);
}
function getPort(){
    var protocol = location.protocol;
    var port = location.port;
   
    if(protocol == "http:"){
        if(port == ""){
            port = 80;
        }else{
       
        }
    }else if(protocol == "https:"){
        if(port == ""){
            port = 443;
        }else{
       
        }       
    }
   
    return port;
}
function getFQDN(){
    var fqdn = "";
    var hostname = location.hostname;
    var protocol = location.protocol;
    var port = location.port;
    var href= location.href;
   
    var a;
    var b;
   
    var lastIndex = href.lastIndexOf("/");
    href = href.substring(0, lastIndex);
   
    if(href.indexOf("~") == -1){
        if(port == ""){
            fqdn = protocol + "//" + hostname + "/"
        }else{
            fqdn = protocol + "//" + hostname + ":" + port + "/"
        }
    }else{
        a = href.split("//")[1];
        b = a.split("/")[1];
        if(port == ""){
            fqdn = protocol + "//" + hostname + "/" + b;
        }else{
            fqdn = protocol + "//" + hostname + ":" + port + "/" + b;
        }
    }
    //fqdn = "http://127.0.0.1:8081/messagebroker/amf";
    return fqdn;
}

function getUserAgent(){
    var ua = navigator.userAgent;
    var strOS;
   
    if(ua.indexOf("Win") >= 0){ strOS = "Win"; }
    else if(ua.indexOf("Mac") >= 0){ strOS = "Mac"; }
    else if(ua.indexOf("Linux") >= 0){ strOS = "Linux"; }
    else{ strOS = ""; }

    return strOS;
}

//------------------------------------------------------------------------------
//-->
</script>
[ EOF ]
----------
 
Tomcatと連携する場合はApacheのコンパイル時に ./configure --enable-proxy --enable-proxy-ajp を有効にし、
/usr/local/apache2/conf/extra/httpd-proxy.conf を新規に作成後、以下を記述保存します。

以下の例では、/struts2/* ( /struts2以下のすべてのリクエスト ) に対して ProxyPass を設定しています。

<Location /struts2/>
ProxyPass ajp://localhost:8009/
</Location>

Apache CXF(JAX-WS) で WS-Security

JAX-WS Client で HTTPS の焼き直しです。
WCFでやった時と同じくらいシンプルな構成に出来たと思います。


まずはサービス側の実装から。

扱うコンテナはCaucho Resin J2EE Server
Apache CXF は 2.2.8 を使用しました。
当然 DIコンテナであるSpringFrameworkの力も借ります。

JDKのkeytool.exeにて証明書を作成しておきます。

[ keys.bat ]
----------------
echo on

set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_20
set PATH=%PATH%;%JAVA_HOME%\bin

keytool.exe -genkey -keyalg RSA -keystore server.keystore

[ EOF ]
----------------

/usr/local/resin/keys/ へ server.keystoreをコピーして下さい。

[ /usr/local/resin/conf/resin.conf (抜粋) ]
----------
    <server-default>
      <!-- The http port -->
      <http address="*" port="8081"/>

      <!--
         - SSL port configuration:
         -
         - <http address="*" port="8443">
         -   <openssl>
         -     <certificate-file>keys/gryffindor.crt</certificate-file>
         -     <certificate-key-file>keys/gryffindor.key</certificate-key-file>
         -     <password>test123</password>
         -   </openssl>
         - </http>
        -->
      <http address="*" port="8443">
         <jsse-ssl>
             <key-store-type>jks</key-store-type>
             <key-store-file>keys/server.keystore</key-store-file>
             <password>password</password>
         </jsse-ssl>
      </http>
[ 省略 ]
----------



サービス側の実装コード。プロジェクト名は ws-sample-service です。

[ IService.java ]
----------
package com.chocbanana.ws;


import javax.jws.WebMethod;
import javax.jws.WebService;
import com.chocbanana.bean.Person;

@WebService
public interface IService {

    @WebMethod
    public String hello();
   
    @WebMethod
    public String sayHello(String message);
   
    @WebMethod
    public Person getPerson();

    @WebMethod
    public void setPerson(Person person);
}
[ EOF ]
----------

[ Service.java ]
----------
package com.chocbanana.ws;


import javax.jws.WebService;
import javax.jws.WebMethod;
import com.chocbanana.bean.Person;

@WebService
public class Service implements IService{

    private Person person;
   
    public Service(){}
   
    @WebMethod
    public String hello(){
        return "Hello";
    }
   
    @WebMethod
    public String sayHello(String message){
        message = "Hello " + message;
        return  message;
    }
   
    @WebMethod
    public Person getPerson(){
        return new Person();
    }
   
    @WebMethod
    public void setPerson(Person person){
        this.person = person;
        System.out.println("age: " + this.person.getAge());
        System.out.println("name: " + this.person.getName());
    }
}
[ EOF ]
----------


[ cxf.xml ]
----------
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans.xsd
                     http://cxf.apache.org/jaxws
                     http://cxf.apache.org/schemas/jaxws.xsd">

    <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="service" implementor="com.chocbanana.ws.Service" address="/Service" />
</beans>
[ EOF ]
----------

[ Person.java ]
----------
package com.chocbanana.bean;

public class Person {
    private String name;
    private int age;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
[ EOF ]
----------

[ web.xml ]
----------
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Service</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/com/chocbanana/ws/cxf.xml</param-value>
    </context-param>


    <servlet>
        <servlet-name>context</servlet-name>
        <servlet-class>
            org.springframework.web.context.ContextLoaderServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>

    <mime-mapping>
        <extension>wsdl</extension>
        <mime-type>text/xml</mime-type>
    </mime-mapping>
</web-app>
[ EOF ]
----------

ブ ラウザで http://127.0.0.1:8081/ws-sample/services/Service?wsdl もしくは https://127.0.0.1:8443/ws-sample/services/Service?wsdl にてWSDLが表示されることを確認します。



ここからクライアント側の実装になります。
別のプロジェクトとして実装します。プロジェクト名は ws-sample-client です。

クライアント側の実装の前に、CXFに付属している wsdl2javaを使用してWSDLからサービス側のスタブを生成します。
そのスタブからサービス側のInterfaceを参照します。WCFで使用した SvcUtil.exe/WSDL.exeと同じ扱いですね。

[ my_wsdl2java.bat ]
----------
echo on

set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_20
set CXF_HOME=D:\share\Spring\apache-cxf-2.2.8
set PATH=%PATH%;%JAVA_HOME%\bin;%CXF_HOME%\bin

wsdl2java -p com.chocbanana.ws http://127.0.0.1:8081/ws-sample/services/Service?wsdl

[ EOF ]
----------

これでスタブが生成されるので、クライアントの実装コードからサービス側のInterfaceやJavaBeanを参照します。
注意するのは、ConfigureSSLOnTheClientクラスにてHTTPSの認証をパスさせている点です。

[ com.chocbanana.ws.client.Client.java ]
----------
package com.chocbanana.ws.client;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.chocbanana.ws.Person;

public class Client {

    public static void main(String args[]) {
        try {
            //サービス側のリモートインターフェースから、サーバー側のメソッドを呼び出します。
            com.chocbanana.ws.IService servicePort =
                (com.chocbanana.ws.IService)new ClassPathXmlApplicationContext("/com/chocbanana/ws/client/bean.xml").getBean("service");
   
            //HTTPS認証
            ConfigureSSLOnTheClient.configureSSLOnTheClient(servicePort);

            System.out.println("please press Enter key...");
            System.in.read();
            String resp = servicePort.sayHello("hoge");
            System.out.println("Server responded with: " + resp);
           
            //wsdl2javaで作成したサービス側のインスタンス
            Person person = servicePort.getPerson();
            person.setAge(10);
            person.setName("hoehoe");
           
            servicePort.setPerson(person);
           
        } catch (Exception e) {
            System.out.println("Message :" + e.getMessage());
            e.printStackTrace();
        }
    }
}

[ EOF ]
----------

[ com.chocbanana.ws.client.ConfigureSSLOnTheClient.java ]
----------
package com.chocbanana.ws.client;

import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

public class ConfigureSSLOnTheClient {

    public static void configureSSLOnTheClient(Object o) {
        org.apache.cxf.endpoint.Client clientProxy = ClientProxy.getClient(o);

        HTTPConduit conduit = (HTTPConduit) clientProxy.getConduit();
        HTTPClientPolicy httpClientPolicy = conduit.getClient();
        httpClientPolicy.setAllowChunking(false);

        String targetAddr = conduit.getTarget().getAddress().getValue();
        if (targetAddr.toLowerCase().startsWith("https:")) {
            TrustManager[] simpleTrustManager = new TrustManager[] { new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                public void checkClientTrusted(
                        java.security.cert.X509Certificate[] certs,
                        String authType) {
                }

                public void checkServerTrusted(
                        java.security.cert.X509Certificate[] certs,
                        String authType) {
                }
            } };
            TLSClientParameters tlsParams = new TLSClientParameters();
            tlsParams.setTrustManagers(simpleTrustManager);
            tlsParams.setDisableCNCheck(true);
            conduit.setTlsClientParameters(tlsParams);
        }
    }
}
[ EOF ]
----------

以下がクライアント側の bean定義ファイル。

[ bean.xml ]
----------
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:sec="http://cxf.apache.org/configuration/security"
    xmlns:http="http://cxf.apache.org/transports/http/configuration"
    xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
    xsi:schemaLocation="
             http://cxf.apache.org/configuration/security
                http://cxf.apache.org/schemas/configuration/security.xsd
           http://cxf.apache.org/transports/http/configuration
              http://cxf.apache.org/schemas/configuration/http-conf.xsd
           http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="proxyFactory"
        class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceClass"
            value="com.chocbanana.ws.IService" />
        <property name="address"
            value="https://127.0.0.1:8443/ws-sample/services/Service" />
    </bean>

    <bean id="service" class="com.chocbanana.ws.IService"
        factory-bean="proxyFactory" factory-method="create" />

</beans>
[ EOF ]
----------

あー、ハマったよ。3日間...いや、もっとかな?
記憶に残ってるのは3日くらいなんですがね...。


えー、WCFでのHTTPS連携はやったけど、今回はそのJava版で。

使うのはApacheCXFです。
例によって J2SEのkeyToolで証明書を作成し、WebコンテナでHTTPS通信させます。

[ keytool.bat ]
-----
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -genkey -alias server -keyalg RSA -keypass password -storepass password -keystore server.keystore
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -certreq -alias server -keystore server.keystore -storepass password -file server.csr
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -export -alias server -keystore server.keystore -storepass password -file server.cer
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -import -alias server -storepass password -file server.cer
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -list -storepass password
[ EOF ]
-----

サービス側のデプロイはコ コを参照してもらうとして、またクライアント側のみ提示しておきます。

サーバー側に配置した証明書と同じものを、クライアントとなるプロジェクト内に配置します。
プロジェクト名は [ ws-sample-client ] とし、認証鍵は [ ws-sample-client/keys/ ] 以下に保存するものとします。
server.cer
server.csr
server.keystore
の3つですか。まぁ使っているのは server.keystoreだけなんですがね。多分keystoreだけでイイハズ。

あとはコードを提示しておきます。
注意点は、サービス側で提供しているInterfaceをクライアント側のプロジェクトにも配置しておくこと...でしょうか。

[ com.chocbanana.ws.IService ]
----------------
package com.chocbanana.ws;


import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public interface IService {

    @WebMethod
    public String hello();
   
    @WebMethod
    public String sayHello(String message);
}
[ EOF ]
----------------


[ com.chocbanana.ws.client.Client ]
----------------
package com.chocbanana.ws.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;

public class Client {

    private static void configureSSLOnTheClient(Object c) {
        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(c);
        HTTPConduit httpConduit = (HTTPConduit) client.getConduit();

        try {
            TLSClientParameters tlsParams = new TLSClientParameters();
            tlsParams.setDisableCNCheck(true);

            KeyStore keyStore = KeyStore.getInstance("JKS");
            String trustpass = "password";

            String contextPath = "";
            try {
                contextPath = new java.io.File("keys").getAbsolutePath();
                // Main.class.getResource(".").toString();
                System.out.println(contextPath);
            } catch (Exception e) {
                e.printStackTrace();
            }

            File truststore = new File(contextPath + "/server.keystore");
            // System.out.println(truststore.getAbsolutePath());

            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            TrustManagerFactory trustFactory = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustFactory.init(keyStore);
            TrustManager[] tm = trustFactory.getTrustManagers();
            tlsParams.setTrustManagers(tm);
            tlsParams.setDisableCNCheck(true);

            KeyManager[] myKeyManagers = getKeyManagers(keyStore, trustpass);
            tlsParams.setKeyManagers(myKeyManagers);

            truststore = new File(contextPath + "/server.keystore");
            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            KeyManagerFactory keyFactory = KeyManagerFactory
                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyFactory.init(keyStore, trustpass.toCharArray());
            KeyManager[] km = keyFactory.getKeyManagers();
            tlsParams.setKeyManagers(km);
            tlsParams.setDisableCNCheck(true);

            TrustManager[] myTrustStoreKeyManagers = getTrustManagers(keyStore);
            tlsParams.setTrustManagers(myTrustStoreKeyManagers);

            FiltersType filter = new FiltersType();
            filter.getInclude().add(".*_EXPORT_.*");
            filter.getInclude().add(".*_EXPORT1024_.*");
            filter.getInclude().add(".*_WITH_DES_.*");
            filter.getInclude().add(".*_WITH_NULL_.*");
            filter.getExclude().add(".*_DH_anon_.*");
            tlsParams.setCipherSuitesFilter(filter);

            httpConduit.setTlsClientParameters(tlsParams);
        } catch (KeyStoreException kse) {
            System.out
                    .println("Security configuration failed with the following: "
                            + kse.getCause());
        } catch (NoSuchAlgorithmException nsa) {
            System.out
                    .println("Security configuration failed with the following: "
                            + nsa.getCause());
        } catch (FileNotFoundException fnfe) {
            System.out
                    .println("Security configuration failed with the following: "
                            + fnfe.getCause());
        } catch (UnrecoverableKeyException uke) {
            System.out
                    .println("Security configuration failed with the following: "
                            + uke.getCause());
            // } catch (CertificateException ce) {
            // System.out.println("Security configuration failed with the
            // following: " + ce.getCause());
        } catch (GeneralSecurityException gse) {
            System.out
                    .println("Security configuration failed with the following: "
                            + gse.getCause());
        } catch (IOException ioe) {
            System.out
                    .println("Security configuration failed with the following: "
                            + ioe.getCause());
        }
    }

    private static TrustManager[] getTrustManagers(KeyStore trustStore)
            throws NoSuchAlgorithmException, KeyStoreException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
        fac.init(trustStore);
        return fac.getTrustManagers();
    }

    private static KeyManager[] getKeyManagers(KeyStore keyStore,
            String keyPassword) throws GeneralSecurityException, IOException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        char[] keyPass = keyPassword != null ? keyPassword.toCharArray() : null;
        KeyManagerFactory fac = KeyManagerFactory.getInstance(alg);
        fac.init(keyStore, keyPass);
        return fac.getKeyManagers();
    }

    public static void main(String args[]) {
        // System.out.println("The client's security configuration will be done
        // programatically.");
        // System.out.println();
        String address = "https://127.0.0.1:8443/ws-sample/services/Service";
        JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
        proxyFactory.setServiceClass(com.chocbanana.ws.IService.class);
        proxyFactory.setAddress(address);

        com.chocbanana.ws.IService client = (com.chocbanana.ws.IService) proxyFactory
                .create();
        configureSSLOnTheClient(client);

        System.out.println("Invoking greetMe...");
        try {
            String resp = client.sayHello("hoge"); // greetMe(System.getProperty("user.name"));
            System.out.println("Server responded with: " + resp);
            System.out.println();

        } catch (Exception e) {
            System.out.println("Invocation failed with the following: "
                    + e.getCause());
            System.out.println();
        }
    }
}
[ EOF ]
----------------

このコードは時間掛かったね。
あー、これでやっと終わった!

nonHTTPSなら話は簡単なのよね、でもセキュリティがどうとか、暗号化されてないとどうとか昨今五月蝿いじゃない?
これなら文句ねーだろ...。


- 応用編 SpringFramework使用 -

[ Client.java ]
---------------
package com.chocbanana.ws.client;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

    public static void main(String args[]) {
        try {
            com.chocbanana.ws.IService servicePort =
                (com.chocbanana.ws.IService)new ClassPathXmlApplicationContext("/com/chocbanana/ws/client/bean.xml").getBean("service");
   
            ConfigureSSLOnTheClient.configureSSLOnTheClient(servicePort);
   
            System.out.println("Invoking greetMe...");
            String resp = servicePort.sayHello("hoge");
            System.out.println("Server responded with: " + resp);
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
[ EOF ]
---------------


[ bean.xml ]
---------------
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schema/jaxws.xsd">

    <bean id="proxyFactory"
        class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceClass"
            value="com.chocbanana.ws.IService" />
        <property name="address"
            value="https://127.0.0.1:8443/ws-sample/services/Service" />
    </bean>

    <bean id="service" class="com.chocbanana.ws.IService"
        factory-bean="proxyFactory" factory-method="create" />

</beans>
[ EOF ]
---------------


[ ConfigureSSLOnTheClient.java ]
---------------
package com.chocbanana.ws.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;

public class ConfigureSSLOnTheClient {
   
    public static void configureSSLOnTheClient(Object c) {
        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(c);
        HTTPConduit httpConduit = (HTTPConduit) client.getConduit();

        try {
            TLSClientParameters tlsParams = new TLSClientParameters();
            tlsParams.setDisableCNCheck(true);

            KeyStore keyStore = KeyStore.getInstance("JKS");
            String trustpass = "password";

            String contextPath = "";
            contextPath = new java.io.File("keys").getAbsolutePath();

            File truststore = new File(contextPath + "/server.keystore");

            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            TrustManagerFactory trustFactory = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustFactory.init(keyStore);
            TrustManager[] tm = trustFactory.getTrustManagers();
            tlsParams.setTrustManagers(tm);
            tlsParams.setDisableCNCheck(true);

            KeyManager[] myKeyManagers = getKeyManagers(keyStore, trustpass);
            tlsParams.setKeyManagers(myKeyManagers);

            truststore = new File(contextPath + "/server.keystore");
            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            KeyManagerFactory keyFactory = KeyManagerFactory
                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyFactory.init(keyStore, trustpass.toCharArray());
            KeyManager[] km = keyFactory.getKeyManagers();
            tlsParams.setKeyManagers(km);
            tlsParams.setDisableCNCheck(true);

            TrustManager[] myTrustStoreKeyManagers = getTrustManagers(keyStore);
            tlsParams.setTrustManagers(myTrustStoreKeyManagers);

            FiltersType filter = new FiltersType();
            filter.getInclude().add(".*_EXPORT_.*");
            filter.getInclude().add(".*_EXPORT1024_.*");
            filter.getInclude().add(".*_WITH_DES_.*");
            filter.getInclude().add(".*_WITH_NULL_.*");
            filter.getExclude().add(".*_DH_anon_.*");
            tlsParams.setCipherSuitesFilter(filter);

            httpConduit.setTlsClientParameters(tlsParams);
        } catch(Exception e){
            e.printStackTrace();
        }
    }

    private static TrustManager[] getTrustManagers(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
        fac.init(trustStore);
        return fac.getTrustManagers();
    }

    private static KeyManager[] getKeyManagers(KeyStore keyStore, String keyPassword) throws GeneralSecurityException, IOException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        char[] keyPass = keyPassword != null ? keyPassword.toCharArray() : null;
        KeyManagerFactory fac = KeyManagerFactory.getInstance(alg);
        fac.init(keyStore, keyPass);
        return fac.getKeyManagers();
    }
}
[  EOF ]
---------------


Visual Studio 2010 beta2で連携してみました。

...使えねー。......使いたくねー。
速度的には問題ないと思うんですが、如何せん作りにくいです。もの凄く。
まぁサーバー側がMSから脱却できるってだけでも御の字でしょうね。
何が悲しくてIISなんぞ...。


先ずはサーバーサイド。Apache CXFです。大体コ コと一緒。

必要になるライブラリ一覧(若干余計なものも混じってますが)

[ WEB-INF\lib ]
---------------
aopalliance-1.0.jar
backport-util-concurrent.jar
cglib-nodep-2.1_3.jar
commons-beanutils.jar
commons-digester.jar
commons-fileupload.jar
commons-io.jar
commons-logging.jar
cxf-2.1.5.jar
FastInfoset-1.2.3.jar
flex-messaging-common.jar
flex-messaging-core.jar
flex-messaging-remoting.jar
geronimo-annotation_1.0_spec-1.1.1.jar
geronimo-servlet_2.5_spec-1.2.jar
geronimo-stax-api_1.0_spec-1.0.1.jar
geronimo-ws-metadata_2.0_spec-1.1.2.jar
ibatis-2.3.4.726.jar
jaxb-api-2.1.jar
jaxb-impl-2.1.9.jar
lib.txt
neethi-2.0.4.jar
opensaml-1.1.jar
pjl-comp-filter-1.6.5.jar
postgresql-8.3-603.jdbc4.jar
saaj-api-1.3.jar
saaj-impl-1.3.2.jar
serializer-2.7.1.jar
spring-aop.jar
spring-beans.jar
spring-context-support.jar
spring-context.jar
spring-core.jar
spring-jdbc.jar
spring-orm.jar
spring-tx.jar
spring-web.jar
sql-map-2.dtd
sql-map-config-2.dtd
wsdl4j-1.6.2.jar
wss4j-1.5.7.jar
wstx-asl-3.2.6.jar
xalan-2.7.1.jar
xml-resolver-1.2.jar
XmlSchema-1.4.5.jar
xmlsec-1.4.2.jar
---------------

[ web.xml ]
---------------
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Service</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/com/chocbanana/ws/cxf.xml</param-value>
    </context-param>

    <!-- Filter config with GZIP -->
    <filter>
        <filter-name>CompressingFilter</filter-name>
        <filter-class>
            com.planetj.servlet.filter.compression.CompressingFilter
        </filter-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>statsEnabled</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CompressingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    <servlet>
        <servlet-name>context</servlet-name>
        <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>


    <mime-mapping>
        <extension>wsdl</extension>
        <mime-type>text/xml</mime-type>
    </mime-mapping>
</web-app>
[EOF]
---------------

[ cxf.xml ]
---------------
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans.xsd
                     http://cxf.apache.org/jaxws
                     http://cxf.apache.org/schemas/jaxws.xsd">

    <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="sl" implementor="com.chocbanana.ws.SilverLight" address="/SilverLight" />
</beans>
[EOF]
---------------

[ ISl.java ]
---------------
package com.chocbanana.ws;

import javax.jws.WebMethod;

public interface ISl {
    
    @WebMethod
    public String getHello(String str);
}
[EOF]
---------------

[ SilverLight.java ]
---------------
package com.chocbanana.ws;

import javax.jws.WebMethod;

public class SilverLight implements ISl {
    
    @WebMethod
    public String getHello(String str){
        return ("Hello " + str + ", Wellcome to SilverLight world !");
    }
}
[EOF]
---------------

ここまでがサーバー側。問題なしでしょう。



ここからSilverLight。
VisualStudio 2010 beta2.0なので、製品版とは多少異なるかもしれません。あしからず。


SilverLightのプロジェクトを作成。今回はC#で。っていうかVBなんぞで作って(作られて)堪るか。
SilverLightでの最大の注釈。Svcutil.exeでのスタブ作成したコードは使用出来ません。
[ サービス参照の追加 ] より、WSDLのURIを指定して下さい。

さて困りました。スタブを作成して開発していた時は普通に呼べたメソッドがCall出来ません。
(もうこの時点で9割方どーでも良くなりました。Flexで良いやn(ry)

解説するのもメンドクサクナッタノデ、ソースコピペして置きます。
ご参考までに。

[ My Documents\Visual Studio 2010\Projects\sa1\sa1 ]
---------------
App.xaml
App.xaml.cs
Bin
MainPage.xaml
MainPage.xaml.cs
obj
Properties
sa1.csproj
sa1.csproj.user
Service References
ServiceReferences.ClientConfig
---------------


[ MainPage.xaml ]
---------------
<UserControl x:Class="sa1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="155,119,0,0" Name="btn_test" VerticalAlignment="Top" Width="75" Click="btn_test_Click" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="12,90,0,0" Name="txt_box" VerticalAlignment="Top" Width="376" />
    </Grid>
</UserControl>
[ EOF ]
---------------


えー、見て分かるように、Webサービスの呼び出しがEventDrivenです。
普通に txt_box.Text = scl.getHello(txt_box.Text); ってやりたいだけでこのコード量。
泣けます。
readConfig();はちょっとしたテクニック...なのかも。


[ MainPage.xaml.cs ]
---------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Xml;
using System.Xml.Linq;
using System.ServiceModel;

namespace sa1
{
    public partial class MainPage : UserControl
    {
        private ServiceRef.SilverLightClient scl;
        private ServiceRef.getHello hello;

        public MainPage()
        {
            InitializeComponent();
        }

        #region CallWebService
        private void CallWebService(string value)
        {
            scl = this.GetSilverLightClient();
            hello = this.GetHelloClient();
            
            scl.getHelloCompleted += new EventHandler<ServiceRef.getHelloCompletedEventArgs>(client_GetHelloCompleted);

            hello.Body.arg0 = value;
            scl.OpenAsync();
            scl.getHelloAsync(hello);
            scl.CloseAsync();

        }
        private ServiceRef.SilverLightClient GetSilverLightClient() {
            ServiceRef.SilverLightClient scl;
            string uri = readConfig();

            if (uri != "")
            {
                if (uri.StartsWith("https://") == true)
                {
                    scl = new ServiceRef.SilverLightClient("SilverLightPort_Security", new EndpointAddress(uri));
                }
                else
                {
                    scl = new ServiceRef.SilverLightClient("SilverLightPort", new EndpointAddress(uri));
                }
            }
            else
            {
                scl = new ServiceRef.SilverLightClient();
            }
            return scl;
        }
        private ServiceRef.getHello GetHelloClient()
        {
            ServiceRef.getHello hello;
            hello = new ServiceRef.getHello(new ServiceRef.getHelloBody());
            return hello;
        }
        private string readConfig()
        {
            Uri docUri = System.Windows.Browser.HtmlPage.Document.DocumentUri;
            Uri svcUri = new Uri(docUri, "/services/SilverLight");
            return svcUri.ToString();
        }
        private void client_GetHelloCompleted(object sender, ServiceRef.getHelloCompletedEventArgs e)
        {
            try
            {
                txt_box.Text = e.Result.Body.@return.ToString();
            }catch(Exception ex){
                MessageBox.Show(ex.InnerException.Message);
            }
        }
        #endregion

        private void btn_test_Click(object sender, RoutedEventArgs e)
        {
            string value = txt_box.Text;

            if (value == "")
            {
                value = "Guest User";
            }
            this.CallWebService(value);
        }
    }
}
[ EOF ]
---------------

なんかさー、仕様的に、EventDriven持ってくるとかオカシくね?
普通にCallしたいだけなんよ?
あー、あれか、Webサービス呼び出し用のコントローラー作って隠蔽しないとダメなのか。
そっか、そっか、そっか...。orz

■ 追記
えー、隠蔽出来ませんでした。Viewとなる.csに直書きです。もう嫌です。なんでこんなクソ仕様...。orz



このファイル (ServiceReferences.ClientConfig) は [Webサービス参照] 時にService Referencesディレクトリと共に生成されます。
scl = new ServiceRef.SilverLightClient("SilverLightPort", new EndpointAddress(uri));
部分にて、Binding名を参照しております。

■追記
あー、一応HTTPSにも対応ですね。

[ ServiceReferences.ClientConfig ]
---------------
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="SilverLightServiceSoapBinding" maxBufferSize="2147483647"
                    maxReceivedMessageSize="2147483647">
                    <security mode="None" />
                </binding>
                <binding name="SilverLightServiceBinding_Security" maxBufferSize="2147483647"
                    maxReceivedMessageSize="2147483647">
                    <security mode="Transport" />
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://127.0.0.1:8080/services/SilverLight"
                binding="basicHttpBinding" bindingConfiguration="SilverLightServiceSoapBinding"
                contract="ServiceRef.SilverLight" name="SilverLightPort" />
            <endpoint address="https://127.0.0.1:8443/services/SilverLight"
                binding="basicHttpBinding" bindingConfiguration="SilverLightServiceBinding_Security"
                contract="ServiceRef.SilverLight" name="SilverLightPort_Security" />
        </client>
    </system.serviceModel>
</configuration>
[ EOF ]
---------------

出来上がりです。

cxf_silverlight.jpg

.NET Framework3.5 SP1 以降。Windows XP 以降に対応です。

WCFでの実装になりますが、WS-Security(HTTPS)を利用する場合、残念ながらこの機能は利用出来ません。
SOAP(HTTP)でなら、GZipでの圧縮が有効になります。


えー、出来ましたので、別 途掲載させて頂きます。m(_"_)m

開発環境は VisualStudio 2008 C# です。


まずはサービス側。

pjl-comp-filter でGzip圧縮します。pjl-comp-filter.jar と web.xmlだけでOKです。

[ web.xml ]
----------------------
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Service</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/com/chocbanana/ws/cxf.xml</param-value>
    </context-param>

    <!-- Filter config with GZIP -->
    <filter>
        <filter-name>CompressingFilter</filter-name>
        <filter-class>
            com.planetj.servlet.filter.compression.CompressingFilter
        </filter-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>statsEnabled</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>CompressingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

   
    <servlet>
        <servlet-name>context</servlet-name>
        <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
        <init-param>
            <param-name>services.configuration.file</param-name>
            <param-value>/WEB-INF/flex/services-config.xml</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>ScheduleStarter</servlet-name>
        <servlet-class>com.chocbanana.schedule.ScheduleStarter</servlet-class>
        <load-on-startup>4</load-on-startup>
    </servlet>

    <servlet>
          <servlet-name>FileUpServlet</servlet-name>
          <servlet-class>com.chocbanana.csv.Upload</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>FileUpServlet</servlet-name>
        <url-pattern>/fileup/*</url-pattern>
    </servlet-mapping>

    <mime-mapping>
        <extension>wsdl</extension>
        <mime-type>text/xml</mime-type>
    </mime-mapping>
</web-app>
[ EOF ]
----------------------

[ com.chocbanana.ws.Service.java ]
----------------------
package com.chocbanana.ws;

import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService
public class Service implements IService{

    public Service(){
    }
       
    @WebMethod
    public String hello(){
        return "Hello";
    }
}
[ EOF ]
----------------------


んで、WCF側。


前準備としてGZipライブラリが必要になるので、それを用意します。

http://www.microsoft.com/Downloads/details.aspx?familyid=ED9C315C-31AC-4CCB-B62B-721AF1E2BFA8&displaylang=en
より WCF_WF_Samples.exe をダウンロードします。

それを解凍した中に、WCFWFCardSpace\WCF\Extensibility\MessageEncoder\Compression\CS があるので、それをVS2008で少し改変してビルドしておきます。
このソリューションのビルドが成功すると GZipEncoder.dll が作成されますので、GZip圧縮にはこのDLLを利用します。

以下改変部分。

-------------
1) GZipMessageEncoderFactory.cs
        //This is the actual GZip encoder
        class GZipMessageEncoder : MessageEncoder
        {
            //static string GZipContentType = "application/x-gzip";
            static string GZipContentType = "text/xml"; // 置換

2) GZIPMessageEncodingBindingElement.cs
        public GZipMessageEncodingBindingElement(MessageEncodingBindingElement messageEncoderBindingElement)
        {
            this.innerBindingElement = messageEncoderBindingElement;
            this.innerBindingElement.MessageVersion = MessageVersion.Soap11; // 追加
        }
-------------


あとは以 前作ったこのコードとapp.configに修正を加えます。
使用するプロジェクトにGZipEncoder.dll をインポートして、Microsoft.ServiceModel.Samplesの参照設定を行って下さい。

以下サンプル用ソースコード。

[ app.config ]
-----------------------
<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <section name="WCF_TEST.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
    </sectionGroup>
  </configSections>
  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ClientCredentialsBehavior">
          <clientCredentials>
            <serviceCertificate>
              <authentication certificateValidationMode="None" revocationMode="NoCheck"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <extensions>
      <bindingElementExtensions>
        <add name="gzipMessageEncoding" type="Microsoft.ServiceModel.Samples.GZipMessageEncodingElement, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </bindingElementExtensions>
    </extensions>

    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_Service" closeTimeout="00:01:00"
          openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
          allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
          maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
          messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
          useDefaultWebProxy="true">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="Transport">
            <transport clientCredentialType="None" proxyCredentialType="None"
              realm="" />
            <message algorithmSuite="Default" />
          </security>
        </binding>
      </basicHttpBinding>
      <customBinding>
        <binding name="ISampleServer">
          <gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />
          <httpTransport manualAddressing="false" authenticationScheme="None"
            bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
            proxyAuthenticationScheme="Basic" realm="" useDefaultWebProxy="false" />
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="https://127.0.0.1:8443/services/Service" behaviorConfiguration="ClientCredentialsBehavior"
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_Service"
        contract="ims.IService" name="BasicHttpBinding_Service" />
      <endpoint address="http://127.0.0.1:8080/services/Service" binding="customBinding"
        bindingConfiguration="ISampleServer" contract="ims.IService"
        name="SoapBinding_Service" />
      <metadata>
        <policyImporters>
          <extension type="Microsoft.ServiceModel.Samples.GZipMessageEncodingBindingElementImporter, GZipEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </policyImporters>
      </metadata>
    </client>
  </system.serviceModel>

  <applicationSettings>
    <WCF_TEST.Properties.Settings>
      <setting name="ProxyServer" serializeAs="String">
        <value>http://proxyserver:8080/</value>
      </setting>
      <setting name="OutputFile" serializeAs="String">
        <value>detault.xml</value>
      </setting>
      <setting name="OutputFileFlag" serializeAs="String">
        <value>FALSE</value>
      </setting>
      <setting name="Debug" serializeAs="String">
        <value>FALSE</value>
      </setting>
      <setting name="ProxyEnabled" serializeAs="String">
        <value>FALSE</value>
      </setting>
      <setting name="Domain" serializeAs="String">
        <value>DOMAIN</value>
      </setting>
      <setting name="AdminUser" serializeAs="String">
        <value>Administrator</value>
      </setting>
      <setting name="AdminPass" serializeAs="String">
        <value>password</value>
      </setting>
      <setting name="AdminDomain" serializeAs="String">
        <value>LONDON</value>
      </setting>
      <setting name="EncryptPassword" serializeAs="String">
        <value>OFF</value>
      </setting>
      <setting name="Binding" serializeAs="String">
        <value>SoapBinding_Service</value>
      </setting>
      <setting name="Address" serializeAs="String">
        <value>https://chocbanana.com/services/Service</value>
      </setting>
      <setting name="SOAP_Address" serializeAs="String">
        <value>http://chocbanana.com/services/Service</value>
      </setting>
    </WCF_TEST.Properties.Settings>
  </applicationSettings>
  <startup>
    <supportedRuntime version="v2.0.50727"/>
  </startup>
</configuration>
[ EOF ]
-----------------------


[ Program.cs ]
-----------------------
using System;
using System.Collections.Generic;
using System.Text;
using ims;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.ServiceModel.Samples;
using System.Net;

namespace WCF_TEST {
    class Program {
        static void Main(string[] args) {
            try {
                show();
            } catch (Exception e) {
                Console.WriteLine("msg: " + e.Message);
                Console.WriteLine("data: " + e.Data);
                Console.WriteLine("src: " + e.Source);
                Console.WriteLine("trace: " + e.StackTrace);
            }
        }

        private static void show() {
            string Address = Properties.Settings.Default.Address;
            string Binding = Properties.Settings.Default.Binding;
            ServiceClient service = null;
            if (Address.StartsWith("https://") && Binding == "BasicHttpBinding_Service") {

                //証明書の確認用EvnetHandlerをコール
                ConfirmCertificatePolicy.ConfirmCertificatePolicy_CallEventHandler();
                ims.ServiceClient proxy = new ims.ServiceClient("BasicHttpBinding_Service",
                    new System.ServiceModel.EndpointAddress(Properties.Settings.Default.Address));
                string proxyEnabled = Properties.Settings.Default.ProxyEnabled;

                if (proxyEnabled == "TRUE") {
                    string proxyServer = Properties.Settings.Default.ProxyServer;
                    BasicHttpBinding httpBindingElement = (BasicHttpBinding)proxy.Endpoint.Binding;
                    httpBindingElement.BypassProxyOnLocal = true;
                    httpBindingElement.UseDefaultWebProxy = false;
                    httpBindingElement.ProxyAddress = new Uri(proxyServer);
                    proxy.Endpoint.Binding = httpBindingElement;
                }

                Console.WriteLine(proxy.hello());
                proxy.Close();

            } else {
                ICollection<BindingElement> bindingElements = new List<BindingElement>();
                HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
                string proxyEnabled = Properties.Settings.Default.ProxyEnabled;
                if (proxyEnabled == "TRUE") {
                    string proxyServer = Properties.Settings.Default.ProxyServer;
                    httpBindingElement.BypassProxyOnLocal = true;
                    httpBindingElement.UseDefaultWebProxy = false;
                    httpBindingElement.ProxyAddress = new Uri(proxyServer);
                }

                //httpBindingElement.AuthenticationScheme = AuthenticationSchemes.Basic;

                GZipMessageEncodingBindingElement compBindingElement = new GZipMessageEncodingBindingElement();
                bindingElements.Add(compBindingElement);
                bindingElements.Add(httpBindingElement);

                CustomBinding myBinding = new CustomBinding(bindingElements);
                myBinding.Name = "SoapBinding_Service";
                myBinding.Namespace = "http://ws.chocbanana.com/";


                // Webサービスインスタンス生成 //
                Address = Properties.Settings.Default.SOAP_Address;
                service = new ims.ServiceClient(myBinding, new EndpointAddress(Address));

                HttpRequestMessageProperty httpRequestProperty = new HttpRequestMessageProperty();

                httpRequestProperty.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip");
                httpRequestProperty.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip");

                using (OperationContextScope scope = new OperationContextScope(service.InnerChannel)) {
                    OperationContext.Current.OutgoingMessageProperties
                    [HttpRequestMessageProperty.Name] = httpRequestProperty;
                    Console.WriteLine(service.hello());
                }
            }
        }
    }
}
[ EOF ]
-----------------------

参考サイトURL
http://www.vistax64.com/indigo/113763-wcf-client-j2ee-server-using-gzip.html


クラスタリングとは異なり冗長性に欠けますが、ApacheのVirtualHostと同じように運用することによって、APサーバーの台数を減らせま す。
まぁ、1台のホストが落ちた場合、芋づる式に全部落ちるわけですが。
一応別々のプロセスに分けて起動するので、干渉はしません。

以下、参考までに。


[ Apache httpd.conf -VirtualHost- ]
----------------------
# im4
<VirtualHost *:80>
    ServerAdmin webmaster@giga-works.com
    DocumentRoot "/home/www/c4"
    ServerName im4.giga-works.com

    ErrorLog "/home/www/c4/logs/demo_error_log"
    CustomLog "/home/www/c4/logs/demo_access_log" common

    <Directory "/home/www/demo">
        Options Indexes FollowSymLinks
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>

    ResinConfigServer 192.168.102.156 6804
    CauchoConfigCacheDirectory /tmp
    CauchoStatus yes
</VirtualHost>
----------------------
必要台数分記述のこと。


ここからResin。

[root@c1 ~]# ls /usr/local/resin/
WEB-INF/  bin/  conf/  extra_conf/  keys/  lib/  libexec/  log/  logs/  php/  run/  webapps/

VirtualHostとして運用する台数分のconfファイルを用意して、
[root@c1 ~]# ls /usr/local/resin/extra_conf/
c1.conf  c2.conf  c3.conf  c4.conf

それぞれの該当箇所を変更。
[root@c1 ~]# cat /usr/local/resin/extra_conf/c4.conf

[ c4.conf変更箇所のみ ]
-----------------------
    <cluster>
      <!--
      - <srun server-id="" host="127.0.0.1" port="6802"/>
      -->
     <srun id="im4" host="192.168.102.156" port="6804" index="1"/>
    </cluster>

    <!-- configures the default host, matching any host name -->
    <host id="im4.giga-works.com" root-directory=".">
      <!--
         - configures an explicit root web-app matching the
         - webapp's ROOT
        -->

      <web-app id="/" document-directory="/home/www/c4/">
        <class-loader>
            <compiling-loader path='WEB-INF/classes' source='WEB-INF/src' batch='true'/>
            <library-loader path='WEB-INF/lib'/>
        </class-loader>
        <config-file>WEB-INF/web.xml</config-file>
      </web-app>
    </host>
[ EOF ]
-----------------------
<srun/>と<host/>でApacheのVirtualHost に対応させる。

c1.conf, c2.conf, c3.confは
<srun id="im-demo" host="192.168.102.156" port="6804" index="1"/>
<host id="im-demo.giga-works.com" root-directory=".">
<web-app id="/" document-directory="/home/www/c4/">
がそれぞれ違うだけ。


[ c4.conf全文 ]
-----------------------
<!--
   - Resin 3.0 configuration file.
  -->
<resin xmlns="http://caucho.com/ns/resin"
       xmlns:resin="http://caucho.com/ns/resin/core">
  <!--
     - Logging configuration for the JDK logging API.
    -->
  <log name="" path="stdout:" timestamp="[%H:%M:%S.%s] "/>

  <logger name="com.caucho.java" level="config"/>
  <logger name="com.caucho.loader" level="config"/>

  <!--
     - For production sites, change dependency-check-interval to something
     - like 600s, so it only checks for updates every 10 minutes.
    -->
  <dependency-check-interval>2s</dependency-check-interval>

  <!--
     - You can change the compiler to "javac" or jikes.
     - The default is "internal" only because it's the most
     - likely to be available.
    -->
  <javac compiler="internal" args=""/>

  <!-- Security providers.
     - <security-provider>
     -    com.sun.net.ssl.internal.ssl.Provider
     - </security-provider>
    -->

  <!--
     - If starting bin/resin as root on Unix, specify the user name
     - and group name for the web server user.
     -
     - <user-name>resin</user-name>
     - <group-name>resin</group-name>
    -->

  <!--
     - Configures threads shared among all HTTP and SRUN ports.
    -->
  <thread-pool>
    <!-- Maximum number of threads. -->
    <thread-max>1024</thread-max>

    <!-- Minimum number of spare connection threads. -->
    <spare-thread-min>10</spare-thread-min>
  </thread-pool>

  <!--
     - Configures the minimum free memory allowed before Resin
     - will force a restart.
    -->
  <min-free-memory>1M</min-free-memory>

  <server>
    <!-- adds all .jar files under the resin/lib directory -->
    <class-loader>
      <tree-loader path="${resin.home}/lib"/>
      <tree-loader path="${server.root}/lib"/>
    </class-loader>

    <!-- Configures the keepalive -->
    <keepalive-max>500</keepalive-max>
    <keepalive-timeout>120s</keepalive-timeout>

    <resin:if test="${resin.isProfessional()}">
      <select-manager enable="true"/>
    </resin:if>

    <!-- listen to the http ports only after the server has started. -->
    <bind-ports-after-start/>

    <!-- The http port -->
    <!--
    - <http server-id="" host="*" port="8080"/>
    -->

    <!--
      - SSL port configuration:
      -
      - <http port="8443">
      -   <openssl>
      -     <certificate-file>keys/gryffindor.crt</certificate-file>
      -     <certificate-key-file>keys/gryffindor.key</certificate-key-file>
      -     <password>test123</password>
      -   </openssl>
      - </http>
    -->
    <!--
    - <http port="443">
    -    <jsse-ssl>
    -        <key-store-type>jks</key-store-type>
    -        <key-store-file>keys/server.key</key-store-file>
    -        <password>password</password>
    -    </jsse-ssl>
    - </http>
    -->

    <!--
       - The local cluster, used for load balancing and distributed
       - backup.
      -->
    <cluster>
      <!--
      - <srun server-id="" host="127.0.0.1" port="6802"/>
      -->
     <srun id="im4" host="192.168.102.156" port="6804" index="1"/>
    </cluster>

    <!--
       - Configures the persistent store for single-server or clustered
       - in Resin professional.
      -->
    <resin:if test="${resin.isProfessional()}">
      <persistent-store type="cluster">
        <init path="session"/>
      </persistent-store>
    </resin:if>

    <!--
       - Enables/disables exceptions when the browser closes a connection.
      -->
    <ignore-client-disconnect>true</ignore-client-disconnect>

    <!--
       - For security, use a different cookie for SSL sessions.
       - <ssl-session-cookie>SSL_JSESSIONID</ssl-session-cookie>
      -->

    <!--
       - Enables the cache (available in Resin Professional)
      -->
    <resin:if test="${isResinProfessional}">
      <cache path="cache" memory-size="8M"/>
    </resin:if>

    <!--
       - Enables periodic checking of the server status.
       -
       - With JDK 1.5, this will ask the JDK to check for deadlocks.
       - All servers can add <url>s to be checked.
      -->
    <resin:if test="${isResinProfessional}">
      <ping>
        <!-- <url>http://localhost:8080/test-ping.jsp</url> -->
      </ping>
    </resin:if>

    <!--
       - Defaults applied to each web-app.
      -->
    <web-app-default>
      <!--
         - Extension library for common jar files.  The ext is safe
         - even for non-classloader aware jars.  The loaded classes
         - will be loaded separately for each web-app, i.e. the class
         - itself will be distinct.
        -->
      <class-loader>
        <tree-loader path="${server.root}/ext-webapp"/>
      </class-loader>

      <!--
         - Sets timeout values for cacheable pages, e.g. static pages.
        -->
      <cache-mapping url-pattern="/" expires="5s"/>
      <cache-mapping url-pattern="*.gif" expires="60s"/>
      <cache-mapping url-pattern="*.jpg" expires="60s"/>

      <!--
         - Servlet to use for directory display.
        -->
      <servlet servlet-name="directory"
              servlet-class="com.caucho.servlets.DirectoryServlet"/>

      <!--
         - Enable EL expressions in Servlet and Filter init-param
        -->
      <allow-servlet-el/>

      <!--
         - for security, disable session URLs by default.
        -->
      <session-config>
        <enable-url-rewriting>false</enable-url-rewriting>
      </session-config>

      <!--
         - For security, set the HttpOnly flag in cookies.
         - <cookie-http-only/>
        -->
    </web-app-default>

    <!--
       - Sample database pool configuration
       -
       - The JDBC name is java:comp/env/jdbc/test
         <database>
           <jndi-name>jdbc/mysql</jndi-name>
           <driver type="org.gjt.mm.mysql.Driver">
             <url>jdbc:mysql://localhost:3306/test</url>
             <user></user>
             <password></password>
            </driver>
            <prepared-statement-cache-size>8</prepared-statement-cache-size>
            <max-connections>20</max-connections>
            <max-idle-time>30s</max-idle-time>
          </database>
      -->
    <!--
       - Default host configuration applied to all virtual hosts.
      -->
    <host-default>
      <class-loader>
        <compiling-loader path="webapps/WEB-INF/classes"/>
        <library-loader path="webapps/WEB-INF/lib"/>
      </class-loader>

      <!--
         - With another web server, like Apache, this can be commented out
         - because the web server will log this information.
        -->
      <!--
      - <access-log path="logs/access.log"
      -      format='%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
      -      rollover-period="1W"/>
      -->

      <access-log path="logs/access.log"
           format='%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
           rollover-size="-1"/>



      <!-- creates the webapps directory for .war expansion -->
      <web-app-deploy path="webapps"/>

      <!-- creates the deploy directory for .ear expansion -->
      <ear-deploy path="deploy">
        <ear-default>
          <!-- Configure this for the ejb server
             -
             - <ejb-server>
             -   <config-directory>WEB-INF</config-directory>
             -   <data-source>jdbc/test</data-source>
             - </ejb-server>
            -->
        </ear-default>
      </ear-deploy>

      <!-- creates the deploy directory for .rar expansion -->
      <resource-deploy path="deploy"/>

      <!-- creates a second deploy directory for .war expansion -->
      <web-app-deploy path="deploy"/>
    </host-default>

    <!-- includes the web-app-default for default web-app behavior -->
    <resin:import path="${resin.home}/conf/app-default.xml"/>

    <!-- configures a deployment directory for virtual hosts -->
    <host-deploy path="hosts">
      <host-default>
        <resin:import path="host.xml" optional="true"/>
      </host-default>
    </host-deploy>

    <!-- configures the default host, matching any host name -->
    <host id="im4.giga-works.com" root-directory=".">
      <!--
         - configures an explicit root web-app matching the
         - webapp's ROOT
        -->

      <web-app id="/" document-directory="/home/www/c4/">
        <class-loader>
            <compiling-loader path='WEB-INF/classes' source='WEB-INF/src' batch='true'/>
            <library-loader path='WEB-INF/lib'/>
        </class-loader>
        <config-file>WEB-INF/web.xml</config-file>
      </web-app>


      <resin:if test="${java.isJava5()}">
        <!--
           - Administration application /resin-admin
           -
           - password is the md5 hash of the password.
           - localhost is true to limit access to the localhost
          -->
        <resin:set var="resin_admin_password"  default=""/>
        <resin:set var="resin_admin_localhost" default="true"/>

        <web-app id="/resin-admin" document-directory="${resin.home}/php/admin"/>
       </resin:if>
    </host>
  </server>
</resin>
[EOF]
-----------------

Resinの起動用Script。
[root@c1 ~]# cat /etc/rc.d/init.d/resin
-----------------
#!/bin/sh
#
# Linux startup script for Resin
#
# chkconfig: 345 85 15
# description: Resin is a Java Web server.
# processname: wrapper.pl
#
# To install, configure this file as needed and copy init.resin
# to /etc/rc.d/init.d as resin.  Then use "# /sbin/chkconfig resin reset"
#
JAVA_HOME=/usr/local/jdk1.6.0
RESIN_HOME=/usr/local/resin
PID=$RESIN_HOME/run/resin.pid

class=com.caucho.server.http.HttpServer

#export CLASSPATH
export JAVA_HOME RESIN_HOME
#
# If you want to start the entire Resin process as a different user,
# set this to the user name.  If you need to bind to a protected port,
# e.g. port 80, you can't use USER, but will need to use bin/resin.
#
USER=
#
# You can change this to $RESIN_HOME/bin/resin if you need Resin to
# bind to port 80, but run as a different user.
#
EXE=$RESIN_HOME/bin/httpd.sh
#
# Sets the commandline arguments.
#
ARGS="-java_home ${JAVA_HOME} -resin_home ${RESIN_HOME}"

case "$1" in
  start)
        echo -n "Starting resin: "
        echo
        #if test -n "$USER"; then
        #  su $USER -c "$EXE -pid $PID start $ARGS"
        #else
        #  $EXE -pid $PID start $ARGS
        #fi
        $EXE -server im1 -conf $RESIN_HOME/extra_conf/c1.conf -pid $RESIN_HOME/run/im1.pid start $ARGS
        $EXE -server im2 -conf $RESIN_HOME/extra_conf/c2.conf -pid $RESIN_HOME/run/im2.pid start $ARGS
        $EXE -server im3 -conf $RESIN_HOME/extra_conf/c3.conf -pid $RESIN_HOME/run/im3.pid start $ARGS
        $EXE -server im4 -conf $RESIN_HOME/extra_conf/c4.conf -pid $RESIN_HOME/run/im4.pid start $ARGS
        echo
        ;;
  stop)
        echo -n "Shutting down resin: "
        echo
        #$EXE -pid $PID stop
        $EXE -server im1 -conf $RESIN_HOME/extra_conf/c1.conf -pid $RESIN_HOME/run/im1.pid stop
        $EXE -server im2 -conf $RESIN_HOME/extra_conf/c2.conf -pid $RESIN_HOME/run/im2.pid stop
        $EXE -server im3 -conf $RESIN_HOME/extra_conf/c3.conf -pid $RESIN_HOME/run/im3.pid stop
        $EXE -server im4 -conf $RESIN_HOME/extra_conf/c4.conf -pid $RESIN_HOME/run/im4.pid stop
        echo
        #rm -f $PID
        rm -f $RESIN_HOME/run/im1.pid
        rm -f $RESIN_HOME/run/im2.pid
        rm -f $RESIN_HOME/run/im3.pid
        rm -f $RESIN_HOME/run/im4.pid
        ;;
  restart)
        $0 stop
        $0 start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit 0
[EOF]
-----------------

ただ、メッチャメモリー食うので、それだけ注意。
まぁ1GBか2GBあれば足りるだろうけど、どのくらいの数を運用出来るかは...やってみないと分かりませんね...。

以上。

Caucho Resin J2EE Server


 Tomcatと並ぶJ2EEコンテナです。

・インストール
Apache Webサーバーをフロントエンドとして使用します
Java(J2SDK)がインストールされていること


$cd /usr/local/src
$wget http://caucho.com/download/resin-3.0.26.tar.gz
$tar xzf resin-3.0.26.tar.gz
$cd ./resin-3.0.26
$./configure --prefix=/usr/local/resin --with-apxs=/usr/local/apache/bin/apxs --with-java-home=/usr/local/jdk1.6.0
$make
$su
#make install
#mkdir /usr/local/resin/run
#cp contrib/init.resin /etc/rc.d/init.d/resin
#chmod +rx /etc/rc.d/init.d/resin



#vi /etc/rc.d/init.d/resin
----------------------------
#!/bin/sh
#
# Linux startup script for Resin
#
# chkconfig: 345 85 15
# description: Resin is a Java Web server.
# processname: wrapper.pl
#
# To install, configure this file as needed and copy init.resin
# to /etc/rc.d/init.d as resin.  Then use "# /sbin/chkconfig resin reset"
#
JAVA_HOME=/usr/local/jdk1.6.0
RESIN_HOME=/usr/local/resin
PID=$RESIN_HOME/run/resin.pid

class=com.caucho.server.http.HttpServer

#export CLASSPATH
export JAVA_HOME RESIN_HOME
#
# If you want to start the entire Resin process as a different user,
# set this to the user name.  If you need to bind to a protected port,
# e.g. port 80, you can't use USER, but will need to use bin/resin.
#
USER=
#
# You can change this to $RESIN_HOME/bin/resin if you need Resin to
# bind to port 80, but run as a different user.
#
EXE=$RESIN_HOME/bin/httpd.sh
#
# Sets the commandline arguments.
#
ARGS="-java_home ${JAVA_HOME} -resin_home ${RESIN_HOME}"

case "$1" in
  start)
        echo -n "Starting resin: "
        if test -n "$USER"; then
          su $USER -c "$EXE -pid $PID start $ARGS"
        else
          $EXE -pid $PID start $ARGS
        fi
        echo
        ;;
  stop)
        echo -n "Shutting down resin: "
        $EXE -pid $PID stop
        echo
        rm -f $PID
        ;;
  restart)
        $0 stop
        $0 start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit 0
[EOF]
---------------------------


vi /usr/local/resin/conf/resin.conf
---------------------------
<!--
   - Resin 3.0 configuration file.
  -->
<resin xmlns="http://caucho.com/ns/resin"
       xmlns:resin="http://caucho.com/ns/resin/core">
  <!--
     - Logging configuration for the JDK logging API.
    -->
  <log name="" path="stdout:" timestamp="[%H:%M:%S.%s] "/>

  <logger name="com.caucho.java" level="config"/>
  <logger name="com.caucho.loader" level="config"/>

  <!--
     - For production sites, change dependency-check-interval to something
     - like 600s, so it only checks for updates every 10 minutes.
    -->
  <dependency-check-interval>2s</dependency-check-interval>

  <!--
     - You can change the compiler to "javac" or jikes.
     - The default is "internal" only because it's the most
     - likely to be available.
    -->
  <javac compiler="internal" args=""/>

  <!-- Security providers.
     - <security-provider>
     -    com.sun.net.ssl.internal.ssl.Provider
     - </security-provider>
    -->

  <!--
     - If starting bin/resin as root on Unix, specify the user name
     - and group name for the web server user.
     -
     - <user-name>resin</user-name>
     - <group-name>resin</group-name>
    -->

  <!--
     - Configures threads shared among all HTTP and SRUN ports.
    -->
  <thread-pool>
    <!-- Maximum number of threads. -->
    <thread-max>1024</thread-max>

    <!-- Minimum number of spare connection threads. -->
    <spare-thread-min>10</spare-thread-min>
  </thread-pool>

  <!--
     - Configures the minimum free memory allowed before Resin
     - will force a restart.
    -->
  <min-free-memory>1M</min-free-memory>

  <server>
    <!-- adds all .jar files under the resin/lib directory -->
    <class-loader>
      <tree-loader path="${resin.home}/lib"/>
      <tree-loader path="${server.root}/lib"/>
    </class-loader>

    <!-- Configures the keepalive -->
    <keepalive-max>500</keepalive-max>
    <keepalive-timeout>120s</keepalive-timeout>

    <resin:if test="${resin.isProfessional()}">
      <select-manager enable="true"/>
    </resin:if>

    <!-- listen to the http ports only after the server has started. -->
    <bind-ports-after-start/>

    <!-- The http port -->
    <!--
    - <http server-id="" host="*" port="8080"/>
    -->

    <!--
      - SSL port configuration:
      -
      - <http port="8443">
      -   <openssl>
      -     <certificate-file>keys/gryffindor.crt</certificate-file>
      -     <certificate-key-file>keys/gryffindor.key</certificate-key-file>
      -     <password>test123</password>
      -   </openssl>
      - </http>
    -->
    <!--  HTTPSの設定。
        Apache + OpenSSLでも可。
        ResinでHTTPS通信を行う場合は jksを使用。
    -->
    <!--
    <http port="443">
        <jsse-ssl>
            <key-store-type>jks</key-store-type>
            <key-store-file>keys/server.key</key-store-file>
            <password>password</password>
        </jsse-ssl>
    </http>
    -->
    
    <!--
       - The local cluster, used for load balancing and distributed
       - backup.
      -->
    <!--
        クラスタリングなど、
        別のHostにResinをインストールしてアプリケーションサーバーを展開する場合に
        IPアドレスとポート番号を指定する
    -->
    <cluster>
      <srun server-id="" host="127.0.0.1" port="6802"/>
    </cluster>

    <!--
       - Configures the persistent store for single-server or clustered
       - in Resin professional.
      -->
    <resin:if test="${resin.isProfessional()}">
      <persistent-store type="cluster">
        <init path="session"/>
      </persistent-store>
    </resin:if>

    <!--
       - Enables/disables exceptions when the browser closes a connection.
      -->
    <ignore-client-disconnect>true</ignore-client-disconnect>

    <!--
       - For security, use a different cookie for SSL sessions.
       - <ssl-session-cookie>SSL_JSESSIONID</ssl-session-cookie>
      -->

    <!--
       - Enables the cache (available in Resin Professional)
      -->
    <resin:if test="${isResinProfessional}">
      <cache path="cache" memory-size="8M"/>
    </resin:if>

    <!--
       - Enables periodic checking of the server status.
       -
       - With JDK 1.5, this will ask the JDK to check for deadlocks.
       - All servers can add <url>s to be checked.
      -->
    <resin:if test="${isResinProfessional}">
      <ping>
        <!-- <url>http://localhost:8080/test-ping.jsp</url> -->
      </ping>
    </resin:if>

    <!--
       - Defaults applied to each web-app.
      -->
    <web-app-default>
      <!--
         - Extension library for common jar files.  The ext is safe
         - even for non-classloader aware jars.  The loaded classes
         - will be loaded separately for each web-app, i.e. the class
         - itself will be distinct.
        -->
      <class-loader>
        <tree-loader path="${server.root}/ext-webapp"/>
      </class-loader>

      <!--
         - Sets timeout values for cacheable pages, e.g. static pages.
        -->
      <cache-mapping url-pattern="/" expires="5s"/>
      <cache-mapping url-pattern="*.gif" expires="60s"/>
      <cache-mapping url-pattern="*.jpg" expires="60s"/>

      <!--
         - Servlet to use for directory display.
        -->
      <servlet servlet-name="directory"
              servlet-class="com.caucho.servlets.DirectoryServlet"/>

      <!--
         - Enable EL expressions in Servlet and Filter init-param
        -->
      <allow-servlet-el/>

      <!--
         - for security, disable session URLs by default.
        -->
      <session-config>
        <enable-url-rewriting>false</enable-url-rewriting>
      </session-config>

      <!--
         - For security, set the HttpOnly flag in cookies.
         - <cookie-http-only/>
        -->
    </web-app-default>

    <!--
       - Sample database pool configuration
       -
       - The JDBC name is java:comp/env/jdbc/test
         <database>
           <jndi-name>jdbc/mysql</jndi-name>
           <driver type="org.gjt.mm.mysql.Driver">
             <url>jdbc:mysql://localhost:3306/test</url>
             <user></user>
             <password></password>
            </driver>
            <prepared-statement-cache-size>8</prepared-statement-cache-size>
            <max-connections>20</max-connections>
            <max-idle-time>30s</max-idle-time>
          </database>
      -->
    <!--
       - Default host configuration applied to all virtual hosts.
      -->
    <host-default>
      <class-loader>
        <compiling-loader path="webapps/WEB-INF/classes"/>
        <library-loader path="webapps/WEB-INF/lib"/>
      </class-loader>

      <!--
         - With another web server, like Apache, this can be commented out
         - because the web server will log this information.
        -->
      <!--
      - <access-log path="logs/access.log"
      -      format='%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
      -      rollover-period="1W"/>
      -->

      <!-- ログファイルのrollover-sizeを-1とすることでローテーションを回避 -->
      <access-log path="logs/access.log"
           format='%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"'
           rollover-size="-1"/>



      <!-- creates the webapps directory for .war expansion -->
      <web-app-deploy path="webapps"/>

      <!-- creates the deploy directory for .ear expansion -->
      <ear-deploy path="deploy">
        <ear-default>
          <!-- Configure this for the ejb server
             -
             - <ejb-server>
             -   <config-directory>WEB-INF</config-directory>
             -   <data-source>jdbc/test</data-source>
             - </ejb-server>
            -->
        </ear-default>
      </ear-deploy>

      <!-- creates the deploy directory for .rar expansion -->
      <resource-deploy path="deploy"/>

      <!-- creates a second deploy directory for .war expansion -->
      <web-app-deploy path="deploy"/>
    </host-default>

    <!-- includes the web-app-default for default web-app behavior -->
    <resin:import path="${resin.home}/conf/app-default.xml"/>

    <!-- configures a deployment directory for virtual hosts -->
    <host-deploy path="hosts">
      <host-default>
        <resin:import path="host.xml" optional="true"/>
      </host-default>
    </host-deploy>

    <!-- configures the default host, matching any host name -->
    <host id="" root-directory=".">
      <!--
         - configures an explicit root web-app matching the
         - webapp's ROOT
        -->

      <!-- 以下が展開するアプリケーションの設定 -->
      <web-app id="/" document-directory="/usr/local/apache/htdocs/">
        <class-loader>
            <compiling-loader path='WEB-INF/classes' source='WEB-INF/src' batch='true'/>
            <library-loader path='WEB-INF/lib'/>
        </class-loader>
        <config-file>WEB-INF/web.xml</config-file>
      </web-app>

      <resin:if test="${java.isJava5()}">
        <!--
           - Administration application /resin-admin
           -
           - password is the md5 hash of the password.
           - localhost is true to limit access to the localhost
          -->
        <resin:set var="resin_admin_password"  default=""/>
        <resin:set var="resin_admin_localhost" default="true"/>

        <web-app id="/resin-admin" document-directory="${resin.home}/php/admin"/>
       </resin:if>
    </host>
  </server>
</resin>
[EOF]
---------------------------
   <!-- SSLをResinで運用する場合は以下を記述。Javaのkeytoolsにて証明書を作成  -->
   <http port="443">
        <jsse-ssl>
            <key-store-type>jks</key-store-type>
            <key-store-file>keys/server.key</key-store-file>
            <password>password</password>
        </jsse-ssl>
    </http>

    <!-- Apache2.2.xにてVirtualHostで運用する場合はsrun部分に
        - 自身のIPアドレスを記述する。ApacheのVirtualHostタグと連動するため、
        - LoopBackアドレスではコネクションエラーになる
    -->
    <cluster>
      <!--
       -  <srun server-id="" host="127.0.0.1" port="6802"/>
      -->
        <srun server-id="" host="192.168.102.155" port="6802"/>
    </cluster>

      <!--
          - document-directoryにアプリケーションを展開するDirectoryを記述する
      -->
      <web-app id="/" document-directory="/usr/local/apache/htdocs/">
        <class-loader>
            <compiling-loader path='WEB-INF/classes' source='WEB-INF/src' batch='true'/>
            <library-loader path='WEB-INF/lib'/>
        </class-loader>
        <config-file>WEB-INF/web.xml</config-file>
      </web-app>


以上、基本的なmemo。

Visual Studio 2008 C# Express Edition の WCF を使用して Apache CXF と連携させます。
んで、ついでにhttpsで経路の暗号化。

動作環境は.NET Framework3.5以上なので、WindowsXP以降になります。
svcutil.exeにて作成したスタブをVS2005のProjectにインポートしてコンパイルすれば、.NET Framework2.0でも動作可能です。
従って、.NET Framework2.0が動作するOSであればOKかと。
コンパイルは通ったけど、Win2kで実行したらエラーになりますた。orz

サーバー側はJavaが動けば何でも良いです。


サーバー側(Java)

WebコンテナはCaucho Resin 3.0.27 J2EE Server + Apache CXF

あらかじめSSLの証明書を作成しておきます。
場所は /usr/local/resin-3.0.27/keys/server.key に保存して下さい。

J2SE6.0 に付属している keytool.exeで作成します。

[ keys.bat ]
----------------
echo on
"C:\Program Files\Java\jdk1.6.0_13\bin\keytool.exe" -genkey -keyalg RSA -keystore keys/server.key
----------------
 
さらに、Resinの設定ファイルである resin.conf( /usr/local/resin-3.0.27/conf/resin.conf ) を書き換えます。

[ resin.conf ]
--------------------
    <!--
       - SSL port configuration:
       -
       - <http port="8443">
       -   <openssl>
       -     <certificate-file>keys/gryffindor.crt</certificate-file>
       -     <certificate-key-file>keys/gryffindor.key</certificate-key-file>
       -     <password>test123</password>
       -   </openssl>
       - </http>
      -->
       <http port="8443">
            <jsse-ssl>
                <key-store-type>jks</key-store-type>
                <key-store-file>keys/server.key</key-store-file>
                <password>password</password>
            </jsse-ssl>
       </http>
--------------------

これで httpsで経路を暗号化して通信することが出来るようになりました。
今回はWebコンテナレベルでHTTPS実装を行いましたが、アプリケーションレベルまで落としても良いと思います。

で、Java(サーバー)側のサービスメソッド。

[ com.chocbanana.ws.Service ]
------------------------------
package com.chocbanana.ws;

import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService
public class Service implements IService{
    public Service(){

    }

    @WebMethod
    public String hello(){
        return "Hello";
    }
}
------------------------------


[ com/chocbanana/ws/cxf.xml ]
------------------------------
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans.xsd
                     http://cxf.apache.org/jaxws
                     http://cxf.apache.org/schemas/jaxws.xsd">

    <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="service" implementor="com.chocbanana.ws.Service" address="/Service" />
</beans>
------------------------------


[ web.xml ]
------------------------------
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Service</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/com/chocbanana/ws/cxf.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>context</servlet-name>
        <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>

    <mime-mapping>
        <extension>wsdl</extension>
        <mime-type>text/xml</mime-type>
    </mime-mapping>
</web-app>
------------------------------




クライアント(WCFホスト)

.NET Framework3.5に付属している svcutil.exeでスタブを作成します。

[ svcutil.bat ]
------------------
echo on

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SvcUtil.exe" /l:cs /n:*,ims http://127.0.0.1:8080/services/Service?wsdl /out:Service.cs
------------------
上記は URIがhttpになっていますが、実際には下記に示すコードでhttpsにて通信します。
スタブを作成するだけなので、svcutil.exeを使用する際はhttpでOK。


[ -C#- app.config ]
--------------------
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="ca2.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <system.serviceModel>
        <services />
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_Service">
                    <security mode="Transport" />
                </binding>
            </basicHttpBinding>
            <wsHttpBinding>
                <binding name="wsHttpBinding_Service">
                    <security mode="TransportWithMessageCredential">
                        <transport clientCredentialType="None" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://127.0.0.1:8443/services/Service" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_Service" contract="ims.IService"
                name="BasicHttpBinding_Service" />
            <endpoint address="https://127.0.0.1:8443/services/Service" binding="wsHttpBinding"
                bindingConfiguration="wsHttpBinding_Service" contract="ims.IService"
                name="wsHttpBinding_Service" />
        </client>
    </system.serviceModel>
    <applicationSettings>
        <ca2.Properties.Settings>
            <setting name="URL" serializeAs="String">
                <value>https://192.168.102.101:443/services/Service</value>
            </setting>
        </ca2.Properties.Settings>
    </applicationSettings>
</configuration>
--------------------
こんなapp.configファイル書けるか!ってな人には、.NET Frameworkに付属しているSvcConfigEditor.exeというツールがありますので、そちらでapp.configファイルを編集してください。Visual Studioにツール登録をしておくと便利です。

詳しくは
http://handcraft.blogsite.org/ComponentGeek/ShowArticle/24.aspx
http://handcraft.blogsite.org/ComponentGeek/ShowArticle/25.aspx

を参照してください。とても参考にさせて頂きました。

SvcConfigEditor.PNG


[ -C#- Program.cs ]
--------------------
using System;
using System.Collections.Generic;
using System.Text;

namespace ca2 {
    class Program {
        static void Main(string[] args) {
            ConfirmCertificatePolicy p = new ConfirmCertificatePolicy(); // 証明書続行確認用
            System.Console.WriteLine("WCFサービスホストを起動したら、キーを入力して下さい。");
            System.Console.ReadLine();
            try {
                ims.ServiceClient proxy = new ims.ServiceClient("BasicHttpBinding_Service",
                    new System.ServiceModel.EndpointAddress(ca2.Properties.Settings.Default.URL));
                Console.WriteLine(proxy.hello());
                proxy.Close();
            } catch (Exception e) {
                Console.WriteLine(e.StackTrace);
            }
            Console.WriteLine("終了するにはなにかキーを押してください。");
            Console.ReadLine();
        }
    }
}
--------------------


[ -C#- ConfirmCertificatePolicy.cs ]
--------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace ca2
{
    class ConfirmCertificatePolicy
    {
        public ConfirmCertificatePolicy()
        {
            ServicePointManager.ServerCertificateValidationCallback += RemoteCertValidation;
        }
        public bool RemoteCertValidation(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
        {
            if (error != SslPolicyErrors.None)
            {
                Console.WriteLine("証明書に問題があります。理由:" + chain.ChainStatus[0].StatusInformation);
                Console.Write("処理を続行しますか? y:続行:");
                string input = Console.ReadLine();
                if (input.ToLower() == "y")
                    return true;
                else
                    return false;
            }
            return true;
        }
    }
}
--------------------

サンプルとしてはこんな感じかな?


手始めにJ2EEコンテナへの展開方法を以下に示します。
XMLを2つ書いて、リモートから呼び出したいJavaのClassを作成します。

[ web.xml ]
----------------
<?xml version="1.0" encoding="UTF-8"?>

<!--
    - START SNIPPET: webxml
-->
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>   
    <servlet>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
        <init-param>
            <param-name>services.configuration.file</param-name>
            <param-value>/WEB-INF/flex/services-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>
</web-app>
[ EOF ]
----------------

[ WEB-INF\flex\services-config.xml ]
----------------
<?xml version="1.0" encoding="UTF-8"?>
<services-config>

    <services>
        <!-- service要素には何でもいいからIDが要る模様 -->
        <service id="remoting-service" class="flex.messaging.services.RemotingService">
            <adapters>
                <adapter-definition id="java-object"
                    class="flex.messaging.services.remoting.adapters.JavaAdapter"
                    default="true" />
            </adapters>
            <default-channels>
                 <!-- HTTP用 -->
                <channel ref="my-amf" />

                <!-- HTTPS用 -->
                <channel ref="my-secure-amf"/>
            </default-channels>

            <!-- destination要素のIDが mx:RemoteObjectの destination プロパティに対応する -->
            <destination id="flexService">
                <properties>
                    <!-- リモート呼び出ししたいJava Beansのクラス名 -->
                   <!-- リモート呼び出しを可能にしたい Beanの数だけ destination要素を記述する -->
                    <!-- 注:この Beanはステートレスである -->
                    <source>com.chocbanana.ws.FlexService</source>
                </properties>
            </destination>
        </service>

    </services>

    <channels>
        <channel-definition id="my-amf"
            class="mx.messaging.channels.AMFChannel">
            <!--
                ここで構成されたエンドポイントURLを mx:RemoteObjectの
                endpoint プロパティにセットする
                {}内は実行時に自動で置き換えられるのでこの記述のままで良い。
            -->
            <endpoint
                url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
                class="flex.messaging.endpoints.AMFEndpoint" />
        </channel-definition>

        <!-- SSL用チャンネルの定義 -->
        <channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
            <endpoint url="https://{server.name}:{server.port}/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/>
            <properties>
                <add-no-cache-headers>false</add-no-cache-headers>
            </properties>
        </channel-definition>
    </channels>
</services-config>
[ EOF ]
----------------

   ...あとは普通にMVCでコーディングしていくだけ。

[ com.chocbanana.ws.FlexService ]
-------------
package com.chocbanana.ws;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class FlexService{
   
    private String dbDriver = null;
    private Db2Interface dao = null;
    private Properties prop = null;
    private static final String contextFile = "com/chocbanana/db/dataSource.xml";
    private static final String contextBean = "db";
   
    public FlexService(){
        prop = new Properties();
        InputStream in = FlexService.class.getResourceAsStream("/com/chocbanana/db/database.properties");
        try{
            prop.load(in);
            dbDriver = prop.getProperty("db.driver");
        }catch(java.io.IOException ioe){
            ioe.printStackTrace();
        }
    }
     ・・・省略・・・
}
[ EOF ]
-------------

 MVCのViewに当たるFlex側で、RemoteObjectを設定すればOK.
MXML内にRemoteObjectを定義して、ActionScriptで呼び出すだけ。

[ App.mxml ]
<mx:Script source="App.as"/>
<mx:RemoteObject id="srv" destination="flexService" endpoint="{'http://{server.name}/messagebroker/amf'}" />

[ App.as ]
--------------
var role:LoginRole = srv.getLoginUser.lastResult as LoginRole;
srv.getLoginUser(uname);
[ EOF ]
--------------

などなど。JavaBeanを戻り値として得ることも可能。


参考になるドキュメントが皆無なのでmemo。

SpringFramework2.5, Apache CXF2.1、その他諸々。
Caucho Resin J2EE Server の設定(/usr/local/resin/resin.conf)

通常は <web-app id="/" document-directory="/hoge/foo/bar"/>  とするところを
<web-app url-regexp='/~([^/]*)' document-directory='/home/www/$1/public_html/'>
とすることで、/home/hoge/public_html/にDocumentDirectoryを設定出来ます。

http://www.example.com/~hoge/としてアクセスした場合の対応方法です。


さらに、<compiling-loader path='WEB-INF/classes' source='WEB-INF/src' batch='true'/>
とすると WEB-INF/src 以下が更新されていた場合、自動的にコンパイルしてくれます。
以下参照。

      <web-app id="/" document-directory="/home/www/resin">
        <class-loader>
            <compiling-loader path='WEB-INF/classes' source='WEB-INF/src' batch='true'/>
            <library-loader path='WEB-INF/lib'/>
        </class-loader>
        <config-file>WEB-INF/web.xml</config-file>
      </web-app>

      <web-app url-regexp='/~([^/]*)' document-directory='/home/www/$1/public_html/'>
        <class-loader>
            <compiling-loader path='WEB-INF/classes' source='WEB-INF/src' batch='true'/>
            <library-loader path='WEB-INF/lib'/>
        </class-loader>
        <config-file>WEB-INF/web.xml</config-file>
      </web-app>
このページの先頭へ