1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package complex.framework.autosave; 25 26 27 28 import com.sun.star.beans.PropertyValue; 29 import com.sun.star.frame.FeatureStateEvent; 30 import com.sun.star.frame.XDispatch; 31 import com.sun.star.frame.XDispatchProvider; 32 import com.sun.star.frame.XModel; 33 import com.sun.star.frame.XStatusListener; 34 import com.sun.star.frame.XStorable; 35 import com.sun.star.lang.XMultiServiceFactory; 36 import com.sun.star.sheet.FillDirection; 37 import com.sun.star.sheet.XCellSeries; 38 import com.sun.star.table.XCellRange; 39 import com.sun.star.util.XCloseable; 40 import com.sun.star.sheet.XSpreadsheet; 41 import com.sun.star.sheet.XSpreadsheetDocument; 42 import com.sun.star.sheet.XSpreadsheets; 43 import com.sun.star.uno.AnyConverter; 44 import com.sun.star.uno.Type; 45 import com.sun.star.uno.UnoRuntime; 46 import com.sun.star.uno.XInterface; 47 import com.sun.star.util.URL; 48 import com.sun.star.util.XURLTransformer; 49 import java.util.*; 50 import util.utils; 51 52 53 // ---------- junit imports ----------------- 54 import org.junit.After; 55 import org.junit.AfterClass; 56 import org.junit.Before; 57 import org.junit.BeforeClass; 58 import org.junit.Test; 59 import org.openoffice.test.OfficeConnection; 60 import util.SOfficeFactory; 61 import static org.junit.Assert.*; 62 // ------------------------------------------ 63 64 //----------------------------------------------- 65 /** @short Check some use cases of the AutoSave feature 66 */ 67 public class AutoSave 68 { 69 //------------------------------------------- 70 class AutoSaveListener implements XStatusListener 71 { 72 private XDispatch m_xAutoSave; 73 private URL m_aRegistration; 74 private Protocol m_aLog; 75 AutoSaveListener(XMultiServiceFactory xSMGR , XDispatch xAutoSave, Protocol aLog )76 public AutoSaveListener(XMultiServiceFactory xSMGR , 77 XDispatch xAutoSave, 78 Protocol aLog ) 79 { 80 m_aLog = aLog; 81 m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "create listener for AutoSave notifications ..."); 82 83 try 84 { 85 m_xAutoSave = xAutoSave; 86 87 XURLTransformer xParser = UnoRuntime.queryInterface(XURLTransformer.class, xSMGR.createInstance("com.sun.star.util.URLTransformer")); 88 URL[] aURL = new URL[1]; 89 aURL[0] = new URL(); 90 aURL[0].Complete = "vnd.sun.star.autorecovery:/doAutoSave"; 91 xParser.parseStrict(aURL); 92 m_aRegistration = aURL[0]; 93 94 m_xAutoSave.addStatusListener(this, m_aRegistration); 95 m_aLog.log(Protocol.TYPE_INFO, "successfully registered as AutoSave listener."); 96 } 97 catch(Throwable ex) 98 { 99 m_aLog.log(ex); 100 } 101 102 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); 103 } 104 disableListener()105 public void disableListener() 106 { 107 m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "stop listening for AutoSave notifications ..."); 108 109 XDispatch xAutoSave = null; 110 URL aRegURL = null; 111 synchronized (this) 112 { 113 xAutoSave = m_xAutoSave; 114 aRegURL = m_aRegistration; 115 } 116 117 try 118 { 119 if ( 120 (xAutoSave != null) && 121 (aRegURL != null) 122 ) 123 xAutoSave.removeStatusListener(this, aRegURL); 124 } 125 catch(Throwable ex) 126 { 127 m_aLog.log(ex); 128 } 129 130 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); 131 } 132 statusChanged(FeatureStateEvent aEvent)133 public void statusChanged(FeatureStateEvent aEvent) 134 { 135 m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "statusChanged() called from AutoSave ..."); 136 137 m_aLog.log("FeatureURL = \""+aEvent.FeatureURL.Complete+"\"" ); 138 m_aLog.log("FeatureDescriptor = \""+aEvent.FeatureDescriptor+"\"" ); 139 m_aLog.log("IsEnabled = \""+aEvent.IsEnabled+"\"" ); 140 m_aLog.log("Requery = \""+aEvent.Requery+"\"" ); 141 m_aLog.log("State:" ); 142 m_aLog.log(aEvent.State ); 143 144 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, ""); 145 } 146 disposing(com.sun.star.lang.EventObject aEvent)147 public void disposing(com.sun.star.lang.EventObject aEvent) 148 { 149 m_aLog.log(Protocol.TYPE_INFO, "disposing() called from AutoSave."); 150 synchronized(this) 151 { 152 m_xAutoSave = null; 153 m_aRegistration = null; 154 } 155 } 156 } 157 158 //------------------------------------------- 159 // some const 160 161 //------------------------------------------- 162 // member 163 164 private Protocol m_aLog; 165 166 /** points to the global uno service manager. */ 167 private XMultiServiceFactory m_xSMGR = null; 168 169 private SOfficeFactory m_aSOF; 170 171 /** can be used to trigger/enable/disable the AutoSave feature. */ 172 private XDispatch m_xAutoSave = null; 173 174 /** a test document, which needs some time for saving to simulate concurrent 175 * save operations. */ 176 private XStorable m_xTestDoc = null; 177 178 private XURLTransformer m_xURLParser = null; 179 180 //------------------------------------------- 181 // test environment 182 183 //------------------------------------------- 184 /** @short A function to tell the framework, 185 which test functions are available. 186 187 @return All test methods. 188 @todo Think about selection of tests from outside ... 189 */ 190 // public String[] getTestMethodNames() 191 // { 192 // return new String[] 193 // { 194 // "checkConcurrentAutoSaveToNormalUISave", 195 // }; 196 // } 197 198 //------------------------------------------- 199 /** @short Create the environment for following tests. 200 201 @descr create an empty test frame, where we can load 202 different components inside. 203 */ before()204 @Before public void before() 205 { 206 m_aLog = new Protocol(Protocol.MODE_HTML | Protocol.MODE_STDOUT, Protocol.FILTER_NONE, utils.getUsersTempDir() + "/complex_log_ascii_01.html"); 207 208 try 209 { 210 // get uno service manager from global test environment 211 m_xSMGR = getMSF(); 212 213 // get another helper to e.g. create test documents 214 m_aSOF = SOfficeFactory.getFactory(m_xSMGR); 215 216 // create AutoSave instance 217 m_xAutoSave = UnoRuntime.queryInterface(XDispatch.class, m_xSMGR.createInstance("com.sun.star.comp.framework.AutoRecovery")); 218 219 // prepare AutoSave 220 // make sure it will be started every 1 min 221 ConfigHelper aConfig = new ConfigHelper(m_xSMGR, "org.openoffice.Office.Recovery", false); 222 aConfig.writeRelativeKey("AutoSave", "Enabled" , Boolean.TRUE ); 223 aConfig.writeRelativeKey("AutoSave", "TimeIntervall", new Integer(1)); // 1 min 224 aConfig.flush(); 225 aConfig = null; 226 227 // is needed to parse dispatch commands 228 m_xURLParser = UnoRuntime.queryInterface(XURLTransformer.class, m_xSMGR.createInstance("com.sun.star.util.URLTransformer")); 229 230 } 231 catch(java.lang.Throwable ex) 232 { 233 m_aLog.log(ex); 234 fail("Couldn't create test environment"); 235 } 236 } 237 238 //------------------------------------------- 239 /** @short close the environment. 240 */ after()241 @After public void after() 242 { 243 // ??? 244 } 245 246 //------------------------------------------- 247 // create a calc document with content, which needs some time for saving createBigCalcDoc()248 private XInterface createBigCalcDoc() 249 { 250 m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "createBigCalcDoc() started ..."); 251 try 252 { 253 m_aLog.log("Create empty calc document for testing."); 254 XSpreadsheetDocument xSheetDoc = m_aSOF.createCalcDoc("_default"); 255 m_aLog.log("Retrieve first sheet from calc document."); 256 XSpreadsheets xSheets = xSheetDoc.getSheets(); 257 XSpreadsheet xSheet = (XSpreadsheet)AnyConverter.toObject( 258 new Type(XSpreadsheet.class), 259 xSheets.getByName( 260 xSheets.getElementNames()[0])); 261 m_aLog.log("Fill two cells with value and formula."); 262 xSheet.getCellByPosition(0, 0).setValue(1); 263 xSheet.getCellByPosition(0, 1).setFormula("=a1+1"); 264 m_aLog.log("Retrieve big range."); 265 XCellRange xRange = xSheet.getCellRangeByName("A1:Z9999"); 266 XCellSeries xSeries = UnoRuntime.queryInterface(XCellSeries.class, xRange); 267 m_aLog.log("Duplicate cells from top to bottom inside range."); 268 xSeries.fillAuto(FillDirection.TO_BOTTOM, 2); 269 m_aLog.log("Duplicate cells from left to right inside range."); 270 xSeries.fillAuto(FillDirection.TO_RIGHT , 1); 271 272 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE | Protocol.TYPE_OK, "createBigCalcDoc() finished."); 273 return xSheetDoc; 274 } 275 catch(Throwable ex) 276 { 277 m_aLog.log(ex); 278 } 279 280 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "createBigCalcDoc() finished."); 281 return null; 282 } 283 284 //------------------------------------------- saveDoc(XInterface xDoc, String sURL)285 private void saveDoc(XInterface xDoc, 286 String sURL) 287 { 288 m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "saveDoc('"+sURL+"') started ..."); 289 try 290 { 291 URL[] aURL = new URL[1]; 292 aURL[0] = new URL(); 293 aURL[0].Complete = ".uno:SaveAs"; 294 m_xURLParser.parseStrict(aURL); 295 296 XModel xModel = UnoRuntime.queryInterface(XModel.class, xDoc); 297 XDispatchProvider xProvider = UnoRuntime.queryInterface(XDispatchProvider.class, xModel.getCurrentController()); 298 XDispatch xDispatch = xProvider.queryDispatch(aURL[0], "_self", 0); 299 300 PropertyValue[] lArgs = new PropertyValue[3]; 301 lArgs[0] = new PropertyValue(); 302 lArgs[0].Name = "URL"; 303 lArgs[0].Value = sURL; 304 lArgs[1] = new PropertyValue(); 305 lArgs[1].Name = "Overwrite"; 306 lArgs[1].Value = Boolean.TRUE; 307 lArgs[2] = new PropertyValue(); 308 lArgs[2].Name = "StoreTo"; 309 lArgs[2].Value = Boolean.TRUE; 310 311 xDispatch.dispatch(aURL[0], lArgs); 312 313 m_aLog.log(Protocol.TYPE_OK, "saveDoc('"+sURL+"') = OK."); 314 } 315 /* 316 catch(com.sun.star.io.IOException exIO) 317 { 318 m_aLog.log(Protocol.TYPE_WARNING , "got IOException on calling doc.store()." ); 319 m_aLog.log(Protocol.TYPE_WARNING_INFO, "Please check the reason for that more in detail." ); 320 m_aLog.log(Protocol.TYPE_WARNING_INFO, "A message like \"Concurrent save requests are not allowed.\" was intended and doesn't show an error!"); 321 m_aLog.log(Protocol.TYPE_WARNING_INFO, "Message of the original exception:" ); 322 m_aLog.log(Protocol.TYPE_WARNING_INFO, exIO.getMessage()); 323 } 324 */ 325 catch(Throwable ex) 326 { 327 m_aLog.log(ex); 328 } 329 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "saveDoc('"+sURL+"') finished."); 330 } 331 332 //------------------------------------------- closeDoc(XInterface xDoc)333 private void closeDoc(XInterface xDoc) 334 { 335 m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "closeDoc() started ..."); 336 337 try 338 { 339 Random aRandom = new Random(); 340 int nRetry = 5; 341 while(nRetry>0) 342 { 343 try 344 { 345 XCloseable xClose = UnoRuntime.queryInterface(XCloseable.class, xDoc); 346 if (xClose != null) 347 { 348 xClose.close(false); 349 m_aLog.log(Protocol.TYPE_OK, "closeDoc() = OK."); 350 nRetry = 0; 351 } 352 else 353 { 354 m_aLog.log(Protocol.TYPE_ERROR, "closeDoc() = ERROR. Doc doesn't provide needed interface!"); 355 } 356 } 357 catch(com.sun.star.util.CloseVetoException exVeto) 358 { 359 m_aLog.log(Protocol.TYPE_WARNING , "got CloseVetoException on calling doc.close()." ); 360 m_aLog.log(Protocol.TYPE_WARNING_INFO, "Please check the reason for that more in detail." ); 361 m_aLog.log(Protocol.TYPE_WARNING_INFO, "A message like \"Can't close while saving.\" was intended and doesn't show an error!"); 362 m_aLog.log(Protocol.TYPE_WARNING_INFO, exVeto.getMessage()); 363 } 364 365 if (nRetry > 0) 366 { 367 --nRetry; 368 long nWait = (long)aRandom.nextInt(30000); // 30 sec. 369 try 370 { 371 m_aLog.log(Protocol.TYPE_INFO, "sleep for "+nWait+" ms"); 372 synchronized(this) 373 { 374 wait(nWait); 375 } 376 } 377 catch(Throwable ex) 378 { 379 m_aLog.log(Protocol.TYPE_WARNING , "got exception for wait() !?"); 380 m_aLog.log(Protocol.TYPE_WARNING_INFO, ex.getMessage()); 381 } 382 } 383 } 384 } 385 catch(Throwable ex) 386 { 387 m_aLog.log(ex); 388 } 389 390 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "closeDoc() finished."); 391 } 392 393 class DocThread extends Thread 394 { DocThread()395 DocThread() 396 {} 397 run()398 public void run() 399 { 400 impl_checkConcurrentAutoSaveToNormalUISave(); 401 } 402 } 403 404 //------------------------------------------- 405 /** @short check concurrent save requests to the same document 406 * at the same time. 407 * 408 * @descr First we simulate an UI save by dispatching the right URL 409 * to the document and at the same time we try to trigger an AutoSave 410 * from another thread. So these operations should be started at the same time. 411 * It should not crash. The AutoSave request must be postphoned. 412 */ checkConcurrentAutoSaveToNormalUISave()413 @Test public void checkConcurrentAutoSaveToNormalUISave() 414 { 415 m_aLog.log(Protocol.TYPE_TESTMARK , "AutoSave"); 416 m_aLog.log(Protocol.TYPE_SCOPE_OPEN, "checkConcurrentAutoSaveToNormalUISave()"); 417 418 AutoSaveListener xListener = new AutoSaveListener(m_xSMGR, m_xAutoSave, m_aLog); 419 420 try 421 { 422 DocThread aThread = new DocThread(); 423 aThread.start(); 424 aThread.join(); 425 } 426 catch(Throwable ex) 427 {} 428 429 xListener.disableListener(); 430 431 m_aLog.log(Protocol.TYPE_SCOPE_CLOSE, "checkConcurrentAutoSaveToNormalUISave()"); 432 m_aLog.logStatistics(); 433 } 434 impl_checkConcurrentAutoSaveToNormalUISave()435 public void impl_checkConcurrentAutoSaveToNormalUISave() 436 { 437 Random aRandom = new Random(); 438 439 int i = 0; 440 int c = 5; 441 for (i=0; i<c; ++i) 442 { 443 XInterface xDoc = createBigCalcDoc(); 444 try 445 { 446 long nWait = (long)aRandom.nextInt(120000); 447 m_aLog.log(Protocol.TYPE_INFO, "sleep for "+nWait+" ms"); 448 synchronized(this) 449 { 450 wait(nWait); 451 } 452 } 453 catch(Throwable ex) 454 { 455 m_aLog.log(Protocol.TYPE_WARNING , "got exception for wait() !?"); 456 m_aLog.log(Protocol.TYPE_WARNING_INFO, ex.getMessage()); 457 } 458 saveDoc(xDoc, utils.getOfficeTemp(m_xSMGR) + "/test_calc.ods"); 459 closeDoc(xDoc); 460 } 461 } 462 getMSF()463 private XMultiServiceFactory getMSF() 464 { 465 final XMultiServiceFactory xMSF1 = UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager()); 466 return xMSF1; 467 } 468 469 // setup and close connections 470 @BeforeClass setUpConnection()471 public static void setUpConnection() throws Exception 472 { 473 System.out.println("setUpConnection()"); 474 connection.setUp(); 475 } 476 477 @AfterClass tearDownConnection()478 public static void tearDownConnection() 479 throws InterruptedException, com.sun.star.uno.Exception 480 { 481 System.out.println("tearDownConnection()"); 482 connection.tearDown(); 483 } 484 private static final OfficeConnection connection = new OfficeConnection(); 485 } 486