Java

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

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>

このページの先頭へ