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