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