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