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 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 073 private XDesktop desktop = null; 074 075 private XComponentContext remoteContext = null; 076 077 /** soffice.binのプロセス */ 078 private Process process = null; 079 080 /** 環境設定のパス */ 081 // 5.1.7.0 (2010/06/01) 複数サーバー対応漏れ 082 public static final String ENV_DIR = HybsSystem.url2dir( StringUtil.nval( HybsSystem.sys( "REPORT_FILE_URL" ) 083 , HybsSystem.sys( "FILE_URL" ) + "REPORT" + File.separator ) 084 + "oooenv" ) + File.separator; 085 private final String envPath; 086 087 /** 環境設定ファイルのID */ 088 private final String envId; 089 090 /** ローカルコンテキスト */ 091 private static XComponentContext xLocalContext = null; 092 static { 093 try { 094 xLocalContext = Bootstrap.createInitialComponentContext( null ); 095 } 096 catch( Throwable th ) { 097 System.out.println( "[ERROR]OOo:Can't start LocalContext,Check OFFICE_HOME!" ); 098 th.printStackTrace(); 099 } 100 } 101 102 /** 103 * コンストラクタです。 104 * 105 * @og.rev 4.3.0.0 (2008/07/15) 設定ファイルを各コンテキストごとに置くように変更 106 * @param id プロセスID 107 */ 108 protected SOfficeProcess( final String id ) { 109 envId = id; 110 // envPath = OFFICE_HOME + "env" + File.separator + envId; 111 envPath = ENV_DIR + envId; 112 } 113 114 /** 115 * OOoへの接続を行います。 116 * 117 * @og.rev 5.0.0.0 (2009/08/03) Linux対応(パイプ名に":"が含まれていると接続できない) 118 * @og.rev 5.1.7.0 (2010/06/01) TCP接続対応 119 */ 120 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 121 protected void bootstrap() { 122 System.out.println( "[INFO]OOo:Starting soffice process,ENV-ID=" + envId ); 123 124 // check enviroment files, if no files, create from default files 125 checkEnv( envPath ); 126 127 // pipe name 128 // 4.3.3.6 (2008/11/15) マルチサーバ対応。同一サーバでの複数実行時不具合のため。 129 // 5.0.0.0 (2009/08/03) Linux対応 130 //String sPipeName = "uno" + envId; 131 String sPipeName = "uno" + "_" + HybsSystem.sys("HOST_URL").replace(':','_').replace('/','_') + "_" + envId; 132 133 // start office process 134 // 5.5.2.4 (2012/05/16) int priority は使われていないので、削除します。 135 process = execOffice( envPath, sPipeName ); 136 System.out.println( "[INFO]OOo:Invoke soffice.bin,ENV-ID=" + envId ); 137 138 // create a URL resolver 139 XUnoUrlResolver xUrlResolver = UnoUrlResolver.create( xLocalContext ); 140 141 // connection string 142 // 5.1.7.0 (2010/06/01) TCP接続対応 143 String sConnect = getConnParam( sPipeName ); 144 145 // wait until office is started 146 try { 147 for( int i = 0;; ++i ) { 148 try { 149 Object context = xUrlResolver.resolve( sConnect ); 150 remoteContext = (XComponentContext) UnoRuntime.queryInterface( XComponentContext.class, context ); 151 if( remoteContext == null ) { throw new BootstrapException( "no component context!" ); } 152 break; 153 } 154 catch( com.sun.star.connection.NoConnectException ex ) { 155 System.out.println( "[INFO]OOo:Waiting for Connect soffice process,ENV-ID=" + envId ); 156 if( i == 60 ) { throw new BootstrapException( ex ); } 157 Thread.sleep( 1000 ); 158 } 159 } 160 161 // create desktop instance 162 XMultiComponentFactory componentFactory = remoteContext.getServiceManager(); 163 desktop = (XDesktop) UnoRuntime.queryInterface( XDesktop.class, componentFactory.createInstanceWithContext( "com.sun.star.frame.Desktop", remoteContext ) ); 164 } 165 catch ( Exception ex ) { 166 throw new HybsSystemException( "[ERROR] Can't create Desktop Instance", ex ); 167 } 168 169 System.out.println( "[INFO]OOo:Connected successful,ENV-ID=" + envId ); 170 } 171 172 /** 173 * Pipe名をキーにOpenOfficeのプロセスに接続するための文字列を生成します。 174 * 175 * @param key Pipe名 176 * 177 * @return 接続文字列 178 */ 179 protected String getConnParam( final String key ) { 180 return "uno:pipe,name=" + key + ";urp;StarOffice.ComponentContext"; 181 } 182 183 /** 184 * デスクトップインスタンスを返します 185 * 186 * @return デスクトップインスタンス 187 */ 188 public XDesktop getDesktop() { 189 return desktop; 190 } 191 192 /** 193 * プロセスを終了します。 194 * また、同時に環境設定用のファイルも削除します。 195 */ 196 public void close() { 197 process.destroy(); 198 FileUtil.deleteFiles( new File( envPath ) ); 199 System.out.println( "[INFO]OOo:Destroy process,ENV-ID=" + envId ); 200 } 201 202 /** 203 * soffice.binを起動します。 204 * 205 * @og.rev 5.1.7.0 (2010/06/01) TCP接続対応 206 * @og.rev 5.5.2.4 (2012/05/16) int priority は使われていないので、削除します。 207 * 208 * @param envPath String 209 * @param pipeName String 210 * 211 * @return soffice.binのプロセス 212 */ 213 private Process execOffice( final String envPath, final String pipeName ) { 214 String[] cmdArray = new String[11]; 215 cmdArray[0] = SOFFICE_BIN; 216 cmdArray[1] = "-nologo"; 217 cmdArray[2] = "-nodefault"; 218 cmdArray[3] = "-norestore"; 219 cmdArray[4] = "-nocrashreport"; 220 cmdArray[5] = "-nolockcheck"; 221 cmdArray[6] = "-minimized"; 222 cmdArray[7] = "-invisible"; 223 cmdArray[8] = "-headless"; 224 cmdArray[9] = "-env:UserInstallation=file:///" + ( envPath ).replace( '\\', '/' ); 225 // 5.1.7.0 (2010/06/01) TCP接続対応 226 cmdArray[10] = getProcParam( pipeName ); 227 228 Process process; 229 try { 230 process = Runtime.getRuntime().exec( cmdArray ); 231 } catch ( IOException ex ) { 232 throw new HybsSystemException( "[ERROR] Cant't exec soffice.bin", ex ); 233 } 234 235 return process; 236 } 237 238 /** 239 * Pipe名をキーにOpenOfficeのプロセスを生成するためのパラメーター文字列を生成します。 240 * 241 * @param key Pipe名 242 * 243 * @return プロセス生成パラメーター 244 */ 245 protected String getProcParam( final String key ) { 246 return "-accept=pipe,name=" + key + ";urp;"; 247 } 248 249 /** 250 * OOoの環境設定ファイルをコピーします。 251 * 252 * ※ OFFICE_HOMEが設定されていない場合、HybsSystemException が、throw されます。 253 * 254 * @og.rev 4.3.0.0 (2008/07/24) OS依存をやめてJavaでコピーする 255 * 256 * @param envPath String 257 */ 258 private void checkEnv( final String envPath ) { 259 260 if( OFFICE_HOME == null || OFFICE_HOME.length() == 0 ) { 261 throw new HybsSystemException( "OFFICE_HOMEが設定されていないため、OpenOfficeを起動できません" ); 262 } 263 264 // 4.3.0.0 (2008/07/24) OS依存からFileUtilを使うように変更 265 FileUtil.copyDirectry( DEFAULT_ENV_PATH, envPath ); 266 267 // 5.1.7.0 (2010/06/01) ファイルマージ対応 268 if( ! ( new File( getTempPath() ) ).mkdirs() ) { 269 System.err.println( "ファイルマージ時のテンポラリフォルダを作成できませんでした。[" + getTempPath() + "]" ); 270 } 271 } 272 273 /** 274 * OpenOfficeのローカルコンポーネントコンテキストを返します。 275 * 276 * @og.rev 5.1.7.0 (2010/06/01) 新規作成 277 * 278 * @return ローカルコンポーネントコンテキスト 279 */ 280 @SuppressWarnings("cast") // OpenOffice 3.2 での冗長なキャスト警告の抑止。キャストをはずすと、旧3.1 では、エラーになる。 281 public XDispatchHelper getDispatcher() { 282 XMultiComponentFactory componentFactory = remoteContext.getServiceManager(); 283 XDispatchHelper dispatcher = null; 284 try { 285 dispatcher = (XDispatchHelper) UnoRuntime.queryInterface( XDispatchHelper.class, componentFactory.createInstanceWithContext( "com.sun.star.frame.DispatchHelper", remoteContext ) ); 286 } 287 catch( com.sun.star.uno.Exception ex ) { 288 throw new HybsSystemException( "ディスパッチャーの取得に失敗しました。", ex ); 289 } 290 return dispatcher; 291 } 292 293 /** 294 * このプロセスに対して固有に使用できる一時ファイルのパスを指定します。 295 * 296 * @og.rev 5.1.7.0 (2010/06/01) 新規作成 297 * 298 * @return 一時ファイルのパス 299 */ 300 public String getTempPath() { 301 return envPath + File.separator + "temp" + File.separator; 302 } 303} 304