    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.File;
    019import java.io.IOException;
    020
    021import org.opengion.fukurou.util.FileUtil;
    022import org.opengion.fukurou.util.StringUtil;
    023import org.opengion.hayabusa.common.HybsSystem;
    024import org.opengion.hayabusa.common.HybsSystemException;
    025
    026import com.sun.star.bridge.UnoUrlResolver;
    027import com.sun.star.bridge.XUnoUrlResolver;
    028import com.sun.star.comp.helper.Bootstrap;
    029import com.sun.star.comp.helper.BootstrapException;
    030import com.sun.star.frame.XDesktop;
    031import com.sun.star.frame.XDispatchHelper;
    032import com.sun.star.lang.XMultiComponentFactory;
    033import com.sun.star.uno.UnoRuntime;
    034import com.sun.star.uno.XComponentContext;
    035
    036/**
    037 * OpenOfficeのプロセスを表すクラスです。
    038 *
    039 * bootstrap()メソッドが呼ばれたタイミングでsoffice.binのプロセスを生成します。
    040 * soffice.binのプロセスを引数なしで実装した場合、通常は各ユーザーで1プロセスしか
    041 * 生成されないため、-env:UserInstallationの引数を指定することで、仮想的に別ユーザー
    042 * として起動しています。
    043 * この"ユーザー"を表すキーは、コンストラクタの引数のidです。
    044 *
    045 * また、この仮想ユーザーで起動した場合、初回起動時にユーザー登録を促す画面が立ち上がります。
    046 * これを回避するため、デフォルトの環境ファイルをプロセス生成前にコピーすることで、認証済みの
    047 * 状態で立ち上がるようにしています。
    048 *
    049 * 起動したプロセスとの通知は名前付きパイプで行われます。パイプ名は、"env"+コンストラクタのidです。
    050 * プロセス起動と、名前付きパイプでの接続は非同期で行われます。
    051 * プロセス起動後、60秒経過しても接続できない場合は、BootstrapExceptionが発生します。
    052 *
    053 * @version  4.0
    054 * @author   Hiroki Nakamura
    055 * @since    JDK5.0,
    056 */
    057public class SOfficeProcess {
    058
    059        /** OOoのインストールディレクトリ  */
    060        public static final String OFFICE_HOME =
    061                ( new File ( System.getenv( "OFFICE_HOME" ) ).getAbsolutePath() ) + File.separator;
    062
    063        /** 設定ファイルの雛形 */
    064        private static final String DEFAULT_ENV_PATH =
    065                OFFICE_HOME + "env" + File.separator + "_default";
    066
    067        /** soffice.binのパス */
    068        private static final String SOFFICE_BIN =
    069                OFFICE_HOME + File.separator + "program" + File.separator + "soffice.bin";
    070
    071        /** ローカルコンテキスト */
    072        private static XComponentContext xLocalContext = null;
    073        static {
    074                try {
    075                        xLocalContext = Bootstrap.createInitialComponentContext( null );
    076                }
    077                catch( Throwable th ) {
    078                        System.out.println( "[ERROR]OOo:Can't start LocalContext,Check OFFICE_HOME!" );
    079                        th.printStackTrace();
    080                }
    081        }
    082
    083        /** リモートデスクトップインスタンス */
    084        @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
    085        private XDesktop desktop        = null;
    086
    087        private XComponentContext remoteContext = null;
    088
    089        /** soffice.binのプロセス */
    090        private Process process         = null;
    091
    092        /** 環境設定のパス */
    093        // 5.1.7.0 (2010/06/01) 複数サーバー対応漏れ
    094//      public static final String ENV_DIR = HybsSystem.url2dir( HybsSystem.sys( "FILE_URL" ) + "oooenv" ) + File.separator;
    095        public static final String ENV_DIR = HybsSystem.url2dir( StringUtil.nval( HybsSystem.sys( "REPORT_FILE_URL" )
    096                                                                                                                                                        , HybsSystem.sys( "FILE_URL" ) + "REPORT" + File.separator )
    097                                                                                                                        + "oooenv" ) + File.separator;
    098        private final String envPath;
    099
    100        /** 環境設定ファイルのID */
    101        private final String envId;
    102
    103        /**
    104         * コンストラクタです。
    105         *
    106         * @og.rev 4.3.0.0 (2008/07/15) 設定ファイルを各コンテキストごとに置くように変更
    107         * @param       id      プロセスID
    108         */
    109        protected SOfficeProcess( final String id ) {
    110                envId = id;
    111                // envPath = OFFICE_HOME + "env" + File.separator + envId;
    112                envPath = ENV_DIR + envId;
    113        }
    114
    115        /**
    116         * OOoへの接続を行います。
    117         *
    118         * @og.rev 5.0.0.0 (2009/08/03) Linux対応(パイプ名に":"が含まれていると接続できない)
    119         * @og.rev 5.1.7.0 (2010/06/01) TCP接続対応
    120         */
    121        @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
    122        protected void bootstrap() {
    123                System.out.println( "[INFO]OOo:Starting soffice process,ENV-ID=" + envId );
    124
    125                // check enviroment files, if no files, create from default files
    126                checkEnv( envPath );
    127
    128                // pipe name
    129                // 4.3.3.6 (2008/11/15) マルチサーバ対応。同一サーバでの複数実行時不具合のため。
    130                // 5.0.0.0 (2009/08/03) Linux対応
    131                //String sPipeName = "uno" + envId;
    132                String sPipeName = "uno" + "_" + HybsSystem.sys("HOST_URL").replace(':','_').replace('/','_') + "_" + envId;
    133
    134                // start office process
    135                // 5.5.2.4 (2012/05/16) int priority は使われていないので、削除します。
    136//              process = execOffice( envPath, sPipeName, 0 );
    137                process = execOffice( envPath, sPipeName );
    138                System.out.println( "[INFO]OOo:Invoke soffice.bin,ENV-ID=" + envId );
    139
    140                // create a URL resolver
    141                XUnoUrlResolver xUrlResolver = UnoUrlResolver.create( xLocalContext );
    142
    143                // connection string
    144                // 5.1.7.0 (2010/06/01) TCP接続対応
    145//              String sConnect = "uno:pipe,name=" + sPipeName + ";urp;StarOffice.ComponentContext";
    146                String sConnect = getConnParam( sPipeName );
    147
    148                // wait until office is started
    149//              XComponentContext xContext = null;
    150                try {
    151                        for( int i = 0;; ++i ) {
    152                                try {
    153                                        Object context = xUrlResolver.resolve( sConnect );
    154                                        remoteContext = (XComponentContext) UnoRuntime.queryInterface( XComponentContext.class, context );
    155                                        if( remoteContext == null ) { throw new BootstrapException( "no component context!" ); }
    156                                        break;
    157                                }
    158                                catch( com.sun.star.connection.NoConnectException ex ) {
    159                                        System.out.println( "[INFO]OOo:Waiting for Connect soffice process,ENV-ID=" + envId );
    160                                        if( i == 60 ) { throw new BootstrapException( ex ); }
    161                                        Thread.sleep( 1000 );
    162                                }
    163                        }
    164
    165                        // create desktop instance
    166                        XMultiComponentFactory componentFactory = remoteContext.getServiceManager();
    167                        desktop = (XDesktop) UnoRuntime.queryInterface( XDesktop.class, componentFactory.createInstanceWithContext( "com.sun.star.frame.Desktop", remoteContext ) );
    168                }
    169                catch ( Exception ex ) {
    170                        throw new HybsSystemException( "[ERROR] Can't create Desktop Instance", ex );
    171                }
    172
    173                System.out.println( "[INFO]OOo:Connected successful,ENV-ID=" + envId );
    174        }
    175
    176        /**
    177         * Pipe名をキーにOpenOfficeのプロセスに接続するための文字列を生成します。
    178         *
    179         * @param key Pipe名
    180         *
    181         * @return 接続文字列
    182         */
    183        protected String getConnParam( final String key ) {
    184                return "uno:pipe,name=" + key + ";urp;StarOffice.ComponentContext";
    185        }
    186
    187        /**
    188         * デスクトップインスタンスを返します
    189         *
    190         * @return デスクトップインスタンス
    191         */
    192        public XDesktop getDesktop() {
    193                return desktop;
    194        }
    195
    196        /**
    197         * プロセスを終了します。
    198         * また、同時に環境設定用のファイルも削除します。
    199         */
    200        public void close() {
    201                process.destroy();
    202                FileUtil.deleteFiles( new File( envPath ) );
    203                System.out.println( "[INFO]OOo:Destroy process,ENV-ID=" + envId );
    204        }
    205
    206        /**
    207         * soffice.binを起動します。
    208         *
    209         * @og.rev 5.1.7.0 (2010/06/01) TCP接続対応
    210         * @og.rev 5.5.2.4 (2012/05/16) int priority は使われていないので、削除します。
    211         *
    212         * @param envPath String
    213         * @param pipeName String
    214         *
    215         * @return soffice.binのプロセス
    216         */
    217//      private Process execOffice( final String envPath, final String pipeName, final int priority ) {
    218        private Process execOffice( final String envPath, final String pipeName ) {
    219                String[] cmdArray = new String[11];
    220                cmdArray[0] = SOFFICE_BIN;
    221                cmdArray[1] = "-nologo";
    222                cmdArray[2] = "-nodefault";
    223                cmdArray[3] = "-norestore";
    224                cmdArray[4] = "-nocrashreport";
    225                cmdArray[5] = "-nolockcheck";
    226                cmdArray[6] = "-minimized";
    227                cmdArray[7] = "-invisible";
    228                cmdArray[8] = "-headless";
    229                cmdArray[9] = "-env:UserInstallation=file:///" + ( envPath ).replace( '\\', '/' );
    230                // 5.1.7.0 (2010/06/01) TCP接続対応
    231//              cmdArray[10] = "-accept=pipe,name=" + pipeName + ";urp;";
    232                cmdArray[10] = getProcParam( pipeName );
    233
    234                Process process;
    235                try {
    236                        process = Runtime.getRuntime().exec( cmdArray );
    237                } catch ( IOException ex ) {
    238                        throw new HybsSystemException( "[ERROR] Cant't exec soffice.bin", ex );
    239                }
    240//              pipe( process.getInputStream(), System.out, "CO> " );
    241//              pipe( process.getErrorStream(), System.err, "CE> " );
    242
    243                return process;
    244        }
    245
    246        /**
    247         * Pipe名をキーにOpenOfficeのプロセスを生成するためのパラメーター文字列を生成します。
    248         *
    249         * @param key Pipe名
    250         *
    251         * @return プロセス生成パラメーター
    252         */
    253        protected String getProcParam( final String key ) {
    254                return "-accept=pipe,name=" + key + ";urp;";
    255        }
    256
    257        /**
    258         * OOoの環境設定ファイルをコピーします。
    259         *
    260         * ※ OFFICE_HOMEが設定されていない場合、HybsSystemException が、throw されます。
    261         *
    262         * @og.rev 4.3.0.0 (2008/07/24) OS依存をやめてJavaでコピーする
    263         *
    264         * @param envPath String
    265         */
    266        private void checkEnv( final String envPath ) {
    267
    268                if( OFFICE_HOME == null || OFFICE_HOME.length() == 0 ) {
    269                        throw new HybsSystemException( "OFFICE_HOMEが設定されていないため、OpenOfficeを起動できません" );
    270                }
    271
    272//              File file = new File( envPath ); // 万が一ファイルが消えていなかった時のため、常にコピー
    273//              if( !file.exists() || !file.isDirectory() ) {
    274//                      String[] cmdArray = new String[7];
    275//                      cmdArray[0] = "xcopy";
    276//                      cmdArray[1] = "/e";
    277//                      cmdArray[2] = "/q";
    278//                      cmdArray[3] = "/y";
    279//                      cmdArray[4] = "/i";
    280//                      cmdArray[5] = DEFAULT_ENV_PATH;
    281//                      cmdArray[6] = envPath;
    282//
    283//                      Process proc = Runtime.getRuntime().exec( cmdArray );
    284//                      proc.waitFor();
    285
    286//              }
    287                // 4.3.0.0 (2008/07/24) OS依存からFileUtilを使うように変更
    288                FileUtil.copyDirectry( DEFAULT_ENV_PATH, envPath );
    289
    290                // 5.1.7.0 (2010/06/01) ファイルマージ対応
    291                if( ! ( new File( getTempPath() ) ).mkdirs() ) {
    292                        System.err.println( "ファイルマージ時のテンポラリフォルダを作成できませんでした。[" + getTempPath() + "]" );
    293                }
    294        }
    295
    296        /**
    297         * OpenOfficeのローカルコンポーネントコンテキストを返します。
    298         *
    299         * @og.rev 5.1.7.0 (2010/06/01) 新規作成
    300         *
    301         * @return ローカルコンポーネントコンテキスト
    302         */
    303        @SuppressWarnings("cast")       // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。
    304        public XDispatchHelper getDispatcher() {
    305                XMultiComponentFactory componentFactory = remoteContext.getServiceManager();
    306                XDispatchHelper dispatcher = null;
    307                try {
    308                        dispatcher = (XDispatchHelper) UnoRuntime.queryInterface( XDispatchHelper.class, componentFactory.createInstanceWithContext( "com.sun.star.frame.DispatchHelper", remoteContext ) );
    309                }
    310                catch( com.sun.star.uno.Exception ex ) {
    311                        throw new HybsSystemException( "ディスパッチャーの取得に失敗しました。", ex );
    312                }
    313                return dispatcher;
    314        }
    315
    316        /**
    317         * このプロセスに対して固有に使用できる一時ファイルのパスを指定します。
    318         *
    319         * @og.rev 5.1.7.0 (2010/06/01) 新規作成
    320         *
    321         * @return 一時ファイルのパス
    322         */
    323        public String getTempPath() {
    324                return envPath + File.separator + "temp" + File.separator;
    325        }
    326}
    327