    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.hayabusa.report2;
    017
    018import java.io.IOException;
    019import java.net.InetSocketAddress;
    020import java.net.Socket;
    021
    022import org.opengion.hayabusa.common.HybsSystemException;
    023
    024/**
    025 * OpenOfficeのプロセスを表すクラスです。
    026 *
    027 * このクラスでは、TCPによりプロセスに接続を行います。
    028 * 基本的には、パイプ名による接続({@link SOfficeProcess})を利用すべきですが、
    029 * x64環境で、64Bit版のJavaを起動した場合、パイプ接続では、UnsatisfiedLinkErrorが発生します。
    030 * このような場合では、TCP接続を利用することで、上記エラーを回避することができます。
    031 *
    032 * @version  4.0
    033 * @author   Hiroki Nakamura
    034 * @since    JDK5.0,
    035 */
    036public final class SOfficeProcessTcp extends SOfficeProcess {
    037
    038        private static final boolean[] ports    = new boolean[512];
    039        private static final Object lock                = new Object();
    040
    041        private final int initPort;
    042        private final int thisPort;
    043
    044        /**
    045         * コンストラクタです。
    046         *
    047         * @param       id                      プロセスID
    048         * @param       initPort        初期ポート
    049         */
    050        protected SOfficeProcessTcp( final String id, final int initPort ) {
    051                super( id );
    052
    053                this.initPort = initPort;
    054                this.thisPort = getThisPort();
    055        }
    056
    057        /**
    058         * TCP接続ポート番号を取得します。
    059         *
    060         * @return TCP接続ポート番号
    061         */
    062        private int getThisPort() {
    063                try {
    064                        Thread.sleep( 100 ); // 切断後すぐにopenされると、ポートチェックで引っかかるため100msWAIT
    065                }
    066                catch( InterruptedException ex ) {
    067                        // ここの Exception は、無視します。
    068                }
    069
    070                int port = -1;
    071                synchronized( lock ) {
    072                        for( int i=0; i<ports.length; i++ ) {
    073                                if( !ports[i] ) {
    074                                        if( checkPort( initPort + i ) ) {
    075//                                              System.out.println( "port=" + ( initPort + i ) + "を使用済みにマークしました。" );
    076                                                ports[i] = true;
    077//                                              System.out.println( "port=" + ( initPort + i ) + "を使用します。" );
    078                                                port = initPort + i;
    079                                                break;
    080                                        }
    081//                                      else {
    082//                                              System.out.println( "port=" + ( initPort + i ) + "は使用中のためスキップします。" );
    083//                                      }
    084                                }
    085                        }
    086                }
    087                if( port < 0 ) {
    088                        throw new HybsSystemException( "TCP接続ポートを取得することができません" );
    089                }
    090
    091                return port;
    092        }
    093
    094        /**
    095         * 引数のポートが使用中かどうかを調べます。
    096         *
    097         * @param port ポート番号
    098         *
    099         * @return      使用中かどうか
    100         */
    101        private boolean checkPort( final int port ) {
    102                boolean flg = false;
    103                Socket sk = null;
    104                try {
    105                        sk = new Socket();
    106                        sk.connect( new InetSocketAddress( "localhost", port ) );
    107                }
    108                catch( IOException ex ) {
    109                        flg = true;
    110                }
    111                finally {
    112                        try {
    113//                              sk.close();
    114                                if( sk != null ) { sk.close(); }        // 5.5.2.6 (2012/05/25) findbugs対応
    115                        }
    116                        catch( IOException ex ) {
    117                                ex.printStackTrace();
    118                        }
    119                }
    120                return flg;
    121        }
    122
    123        /**
    124         * Pipe名をキーにOpenOfficeのプロセスに接続するための文字列を生成します。
    125         * ※TCP接続の場合、キーのPipe名は無視され、内部的に管理されるポート番号一覧より
    126         *   接続ポートを取得します。
    127         *
    128         * @param key Pipe名(無視されます)
    129         *
    130         * @return 接続文字列
    131         */
    132        @Override
    133        protected String getConnParam( final String key ) {
    134                System.out.println( "[INFO]OOo:TCP Connection Start,port=" + thisPort );
    135//              return "uno:socket,host=localhost,tcpNoDelay=1,port=" + String.valueOf( thisPort ) + ";urp;StarOffice.ComponentContext";
    136                return "uno:socket,host=localhost,tcpNoDelay=1,port=" + thisPort + ";urp;StarOffice.ComponentContext";
    137        }
    138
    139        /**
    140         * Pipe名をキーにOpenOfficeのプロセスを生成するためのパラメーター文字列を生成します。
    141         * ※TCP接続の場合、キーのPipe名は無視され、内部的に管理されるポート番号一覧より
    142         *   接続ポートを取得します。
    143         *
    144         * @param key Pipe名(無視されます)
    145         *
    146         * @return プロセス生成パラメーター
    147         */
    148        @Override
    149        protected String getProcParam( final String key ) {
    150//              return "-accept=socket,host=localhost,port=" + String.valueOf( thisPort ) + ";urp;";
    151                return "-accept=socket,host=localhost,port=" + thisPort + ";urp;";
    152        }
    153
    154        /**
    155         * プロセスを終了します。
    156         * また、同時に環境設定用のファイルも削除します。
    157         * ここでは、プロセスを終了すると同時に、そのプロセスのポート番号を開放し、
    158         * 次に起動されるプロセスで利用できるようにします。
    159         */
    160        @Override
    161        public void close() {
    162                super.close();
    163                synchronized( lock ) {
    164                        ports[thisPort-initPort] = false;
    165                }
    166//              System.out.println( "[INFO]OOo:TCP Connection End(Release),port=" + String.valueOf( thisPort ) );
    167                System.out.println( "[INFO]OOo:TCP Connection End(Release),port=" + thisPort );
    168        }
    169}
    170