    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;
    023import org.opengion.fukurou.system.ThrowUtil;                                                   // 6.4.2.0 (2016/01/29)
    024
    025/**
    026 * OpenOfficeのプロセスを表すクラスです。
    027 *
    028 * このクラスでは、TCPによりプロセスに接続を行います。
    029 * 基本的には、パイプ名による接続({@link SOfficeProcess})を利用すべきですが、
    030 * x64環境で、64Bit版のJavaを起動した場合、パイプ接続では、UnsatisfiedLinkErrorが発生します。
    031 * このような場合では、TCP接続を利用することで、上記エラーを回避することができます。
    032 *
    033 * @version  4.0
    034 * @author   Hiroki Nakamura
    035 * @since    JDK5.0,
    036 */
    037public final class SOfficeProcessTcp extends SOfficeProcess {
    038
    039        private static final boolean[] PORTS    = new boolean[512];             // 6.4.1.1 (2016/01/16) ports → PORTS refactoring
    040        private static final Object LOCK                = new Object();                 // 6.4.1.1 (2016/01/16) lock  → LOCK refactoring
    041
    042        private final int initPort;
    043        private final int thisPort;
    044
    045        /**
    046         * コンストラクタです。
    047         *
    048         * @param       id                      プロセスID
    049         * @param       initPort        初期ポート
    050         */
    051        protected SOfficeProcessTcp( final String id, final int initPort ) {
    052                super( id );
    053
    054                this.initPort = initPort;
    055                this.thisPort = getThisPort();
    056        }
    057
    058        /**
    059         * TCP接続ポート番号を取得します。
    060         *
    061         * @return TCP接続ポート番号
    062         */
    063        private int getThisPort() {
    064                try {
    065                        Thread.sleep( 100 ); // 切断後すぐにopenされると、ポートチェックで引っかかるため100msWAIT
    066                }
    067                catch( final InterruptedException ex ) {
    068                        // ここの Exception は、無視します。
    069                }
    070
    071                int port = -1;
    072                synchronized( LOCK ) {
    073                        for( int i=0; i<PORTS.length; i++ ) {
    074                                // 6.0.0.1 (2014/04/25) These nested if statements could be combined
    075                                if( !PORTS[i] && checkPort( initPort + i ) ) {
    076                                        PORTS[i] = true;
    077                                        port = initPort + i;
    078                                        break;
    079                                }
    080                        }
    081                }
    082                if( port < 0 ) {
    083                        throw new HybsSystemException( "TCP接続ポートを取得することができません" );
    084                }
    085
    086                return port;
    087        }
    088
    089        /**
    090         * 引数のポートが使用中かどうかを調べます。
    091         *
    092         * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
    093         *
    094         * @param port ポート番号
    095         *
    096         * @return      使用中かどうか
    097         */
    098        private boolean checkPort( final int port ) {
    099                boolean flg = false;
    100                Socket sk = null;
    101                try {
    102                        sk = new Socket();
    103                        sk.connect( new InetSocketAddress( "localhost", port ) );
    104                }
    105                catch( final IOException ex ) {
    106                        flg = true;
    107                }
    108                finally {
    109                        try {
    110                                if( sk != null ) { sk.close(); }        // 5.5.2.6 (2012/05/25) findbugs対応
    111                        }
    112                        catch( final IOException ex ) {
    113                                System.err.println( ThrowUtil.ogStackTrace( ex ) );                             // 6.4.2.0 (2016/01/29)
    114                        }
    115                }
    116                return flg;
    117        }
    118
    119        /**
    120         * Pipe名をキーにOpenOfficeのプロセスに接続するための文字列を生成します。
    121         * ※TCP接続の場合、キーのPipe名は無視され、内部的に管理されるポート番号一覧より
    122         *   接続ポートを取得します。
    123         *
    124         * @param key Pipe名(無視されます)
    125         *
    126         * @return 接続文字列
    127         * @og.rtnNotNull
    128         */
    129        @Override
    130        protected String getConnParam( final String key ) {
    131                System.out.println( "[INFO]OOo:TCP Connection Start,port=" + thisPort );
    132                return "uno:socket,host=localhost,tcpNoDelay=1,port=" + thisPort + ";urp;StarOffice.ComponentContext";
    133        }
    134
    135        /**
    136         * Pipe名をキーにOpenOfficeのプロセスを生成するためのパラメーター文字列を生成します。
    137         * ※TCP接続の場合、キーのPipe名は無視され、内部的に管理されるポート番号一覧より
    138         *   接続ポートを取得します。
    139         *
    140         * @param key Pipe名(無視されます)
    141         *
    142         * @return プロセス生成パラメーター
    143         * @og.rtnNotNull
    144         */
    145        @Override
    146        protected String getProcParam( final String key ) {
    147                return "-accept=socket,host=localhost,port=" + thisPort + ";urp;";
    148        }
    149
    150        /**
    151         * プロセスを終了します。
    152         * また、同時に環境設定用のファイルも削除します。
    153         * ここでは、プロセスを終了すると同時に、そのプロセスのポート番号を開放し、
    154         * 次に起動されるプロセスで利用できるようにします。
    155         */
    156        @Override
    157        public void close() {
    158                super.close();
    159                synchronized( LOCK ) {
    160                        PORTS[thisPort-initPort] = false;
    161                }
    162                System.out.println( "[INFO]OOo:TCP Connection End(Release),port=" + thisPort );
    163        }
    164}