예제

본 장에서는 예제를 통해 외부에서 OSC 애플리케이션 서버에 있는 프로그램인 OpenFrame CTG를 실행하는 방법에 대해서 기술한다.

1. JCA로 작성한 EJB 2.0 예제

다음 예제는 서블릿에서 EJB를 호출하여 데이터베이스의 테이블에 값을 Insert하고, OSC 서버의 특정 프로그램을 호출하는 동작을 하나의 트랜잭션으로 처리하는 예제이다.

<ejb/server/CtgXa.java>

package ejb.server;

import javax.ejb.Remote;

@Remote
public interface CtgXa{
    String doXATest(String data);
}

EJB의 실제 동작을 구현한 예제이다.

BMT(Bean Management Transaction) 방식으로 UserTransaction 을 생성하고, DB Insert와 OSC Region 서버에 등록된 프로그램 호출을 수행하는 과정을 하나의 트랜잭션으로 완료한다.

<ejb/server/CtgXaBean.java>

package ejb.server;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.annotation.Resource;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.resource.ResourceException;
import javax.resource.cci.ConnectionFactory;
import javax.resource.cci.Interaction;
import javax.resource.cci.InteractionSpec;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

import com.tmax.ofctg.connector.cics.CICSResourceException;
import com.tmax.ofctg.connector.cics.ECIConnectionSpec;
import com.tmax.ofctg.connector.cics.ECIInteractionSpec;
import com.tmax.ofctg.connector.cics.ECIResourceAdapterRc;

@Stateless(mappedName="ejb.server.DBTest")
@TransactionManagement(TransactionManagementType.BEAN)
public class CtgXaBean implements CtgXa
{
    @Resource
    SessionContext ejbContext;

    public String doXATest(String data) {
        UserTransaction tx = null;
        String dbResult = null;
        String ctgResult = null;

        try {
            tx = this.ejbContext.getUserTransaction();
            tx.begin();

            dbResult = insertToDatabase("INSERT INTO CTG_TEST2 VALUES('TST7')");
            ctgResult = insertToCTG(data);

            tx.commit();
            System.out.println("Transaction Committed.");

        } catch (Exception e) {
            try {
                tx.rollback();
                System.out.println("Rollback Succeeded.");
            } catch (IllegalStateException e1) {
                e1.printStackTrace();
                System.out.println("Rollback failed.");
            } catch (SecurityException e1) {
                e1.printStackTrace();
                System.out.println("Rollback failed.");
            } catch (SystemException e1) {
                e1.printStackTrace();
                System.out.println("Rollback failed.");
            }
        }
        return "DB Result = " + dbResult + " <br> Result2 = " + ctgResult;
    }

    public String insertToDatabase(String query) {
        Connection conn = null;
        Statement stmt = null;
        String result = null;

        try {
            Context ctx = new InitialContext();
            DataSource ds = (DataSource) ctx.lookup("tibero_xa_datasource");
            conn = ds.getConnection();

            stmt = conn.createStatement();
            int count = stmt.executeUpdate(query);
            if (count > 0)
                result = String.valueOf(count);

        } catch (SQLException sqle) {
        } catch (Exception e) {
        } finally {
            try {
                if (stmt != null)
                    stmt.close();
                if (conn != null)
                    conn.close();
            } catch (SQLException e) {
            }
        }
        return result;
    }

    public String insertToCTG(String commarea) {
        javax.resource.cci.Connection conn = null;
        String returnStr = null;
        String functionName = "PIVPECIT";

        try {
            InitialContext ctx = new InitialContext();
            ConnectionFactory connFactory = (ConnectionFactory) ctx.lookup("CTGConnector");

            ECIConnectionSpec cs = new ECIConnectionSpec();
            conn = connFactory.getConnection(cs);
            Interaction action = conn.createInteraction();

            ECIInteractionSpec eciiSpec = new ECIInteractionSpec();
            eciiSpec.setUserID("");
            eciiSpec.setPassword("");
            eciiSpec.setFunctionName(functionName);
            eciiSpec.setCommareaLength(5000);
            eciiSpec.setInteractionVerb(InteractionSpec.SYNC_SEND_RECEIVE);

            GenericRecord sndRecord = new GenericRecord(commarea.getBytes("UTF-8"));
            GenericRecord rcvRecord = new GenericRecord();

            if (action.execute(eciiSpec, sndRecord, rcvRecord)) {
                byte[] result = rcvRecord.getCommarea();
                returnStr = new String(result, "UTF-8");
            }
            System.out.println("execute CTG Successed.");

        } catch (CICSResourceException e) {
            String abendCode = e.getAbendCode();
            int respCode = e.getCicsRespCode();

            if (respCode != ECIResourceAdapterRc.ECI_NO_ERROR)
                returnStr = "Func: " + functionName + ", Abend: " + abendCode +
                ", Resp: " + ECIResourceAdapterRc.getErrorMessage(respCode);
            else
                returnStr = "Func: " + functionName + ", Abend: " + abendCode;

            System.out.println("execute CTG failed, " + returnStr);

        } catch (ResourceException e) {
            e.printStackTrace();
            return e.getMessage();

        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();

        } finally {
            try {
                if (conn != null)
                    conn.close();
            } catch (ResourceException e) {
                e.printStackTrace();
            }
        }

        return returnStr;
    }
}

2. COMMAREA 버퍼 클래스

OSC 서버와 통신할 때 사용하는 COMMAREA 버퍼를 나타내는 클래스이다. 반드시 javax.resource.cci.Streamable 인터페이스를 구현해야 한다.

<ejb/server/GenericRecord.java>

package ejb.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.resource.cci.Record;
import javax.resource.cci.Streamable;

public class GenericRecord implements Streamable, Record {
    private static final long serialVersionUID = -7600580218655773637L;
    private byte[] commarea = null;
    private String recordName;

    public GenericRecord() {
    }

    public GenericRecord(byte[] comm) {
        setCommarea(comm);
    }

    public void setCommarea(byte[] comm) {
        commarea = (byte[]) comm.clone();
    }

    public byte[] getCommarea() {
        return commarea;
    }

    public void read(InputStream in) throws IOException {
        commarea = null;

        int total = in.available();
        if (total > 0) {
            commarea = new byte[total];
            in.read(commarea);
        }
    }

    public void write(OutputStream out) throws IOException {
        if (commarea != null) {
            out.write(commarea);
            out.flush();
        }
    }

    @Override
    public GenericRecord clone() throws CloneNotSupportedException {
        return new GenericRecord(getCommarea());
    }

    @Override
    public String getRecordName() {
        return recordName;
    }

    @Override
    public void setRecordName(String recordName) {
        this.recordName = recordName;
    }

    @Override
    public void setRecordShortDescription(String arg0) {
    }

    @Override
    public String getRecordShortDescription() {
        return null;
    }
}

3. 서블릿 프로그램

다음은 EJB를 호출하는 서블릿 프로그램이다. CtgXa의 doXATest()를 호출하고 그 결과를 웹브라우저 화면에 출력한다.

<ejb/client/HelloClient.java>

package ejb.client;

import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import ejb.server.CtgXa;

public class HelloClient extends HttpServlet {
    private static final long serialVersionUID = 5385358571284268459L;
    @EJB(mappedName="ejb.server.CtgXa")
    private CtgXa ctgXaTest;

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        PrintWriter out = response.getWriter();
        try {
            String result = ctgXaTest.doXATest("TestData");

            response.setContentType("text/html");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>HelloClient</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>" + result + "</h1>");
            out.println("</body>");
            out.println("</html>");
            out.close();

        } catch(Exception ex){
            response.setContentType("text/plain");
            ex.printStackTrace(out);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        processRequest(request, response);
    }
}

4. 리소스 어댑터 설정 파일

다음은 리소스 어댑터 설정 파일이다. 예제의 각 config-property-name 항목은 리소스 어댑터 구성 클래스에 명시된 리소스 어댑터 설정 항목을 참고한다.

사용자가 테스트 환경에 맞추어서 host, host_port, regionName, txHelperClassName 항목은 반드시 설정해야 한다.

<ra.xml>

<?xml version="1.0" encoding="UTF-8"?>
<connector id="Connector_ID" version="1.5"
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/connector_1_5.xsd">
  <description>OFCTG Resource Adapter</description>
  <display-name>OFCTG_RA</display-name>
  <vendor-name>TmaxSoft</vendor-name>
  <eis-type>TP Monitor</eis-type>
  <resourceadapter-version>1.0.0.0</resourceadapter-version>
  <resourceadapter>
    <resourceadapter-class>
      com.tmax.connector.spi.TmaxResourceAdapterImpl
    </resourceadapter-class>
    <config-property>
      <config-property-name>port</config-property-name>
      <config-property-type>java.lang.Integer</config-property-type>
      <config-property-value>6557</config-property-value>
    </config-property>
    <config-property>
      <config-property-name>fdlfile</config-property-name>
      <config-property-type>java.lang.String</config-property-type>
      <config-property-value>/home/openframe/samples/tmax.fdl</config-property-value>
    </config-property>
    <outbound-resourceadapter>
      <connection-definition>
        <managedconnectionfactory-class>
          com.tmax.ofctg.connector.cics.ECIManagedConnectionFactory
        </managedconnectionfactory-class>
        <connectionfactory-interface>
          javax.resource.cci.ConnectionFactory
        </connectionfactory-interface>
        <connectionfactory-impl-class>
          com.tmax.ofctg.connector.cics.ECIConnectionFactory
        </connectionfactory-impl-class>
        <connection-interface>
          javax.resource.cci.Connection
        </connection-interface>
        <connection-impl-class>
          com.tmax.ofctg.connector.cics.ECIConnection
        </connection-impl-class>
        <config-property>
          <config-property-name>host</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>192.168.100.200</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>host_port</config-property-name>
          <config-property-type>java.lang.Integer</config-property-type>
          <config-property-value>9000</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>backup</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>192.168.100.201</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>backup_port</config-property-name>
          <config-property-type>java.lang.Integer</config-property-type>
          <config-property-value>9000</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>support_xa</config-property-name>
          <config-property-type>java.lang.Boolean</config-property-type>
          <config-property-value>true</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>txHelperClassName</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>jeus.transaction.TxHelper</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>regionName</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>OSCOIVP1</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>headerType</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>extendedV4</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>connectTimeout</config-property-name>
          <config-property-type>java.lang.Integer</config-property-type>
          <config-property-value>30000</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>fdlFile</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>/home/openframe/samples/tmax.fdl</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>xidMapperPath</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>/home/openframe/xalog/</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>xidMapperFile</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>webtxidmapper</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>logLevel</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>DEV</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>logDir</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>/home/openframe/log/</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>logFile</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>webt.log</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>logValidDays</config-property-name>
          <config-property-type>java.lang.Integer</config-property-type>
          <config-property-value>1</config-property-value>
        </config-property>
        <config-property>
          <config-property-name>connectionName</config-property-name>
          <config-property-type>java.lang.String</config-property-type>
          <config-property-value>tmax1</config-property-value>
        </config-property>
      </connection-definition>
      <transaction-support>XATransaction</transaction-support>
      <reauthentication-support>false</reauthentication-support>
    </outbound-resourceadapter>
    <inbound-resourceadapter>
      <messageadapter>
        <messagelistener>
          <messagelistener-type>
            javax.resource.cci.MessageListener
          </messagelistener-type>
          <activationspec>
            <activationspec-class>
              com.tmax.connector.spi.TmaxActivationSpec
            </activationspec-class>
            <required-config-property>
              <config-property-name>serviceName</config-property-name>
            </required-config-property>
          </activationspec>
        </messagelistener>
      </messageadapter>
    </inbound-resourceadapter>
  </resourceadapter>
</connector>