Add application code to check for and recover from SAP Mobile Server failures.
It is highly recommended that you add a catch call to all synchronize methods (synchronize(), begingSynchronize(), and so on) within your applications to allow the application to recover if SAP Mobile Server fails and needs to be restored from an older database. If not, you may have to reinstall the application manually for all users so they can resynchronize with SAP Mobile Server.
See Restoring from an Older Backup Database File (Data Loss) in the System Administration Guide for information about SAP Mobile Server recovery.
public void startApplication( ) { Application app = Application.getInstance(); Application.getInstance().setApplicationCallback(new MyApplicationCallback()); try { ConnectionProperties connProperties = app.getConnectionProperties(); connProperties.setServerName (“mega-vm008” ); connProperties.setPortNumber (5001); connProperties.setLoginCredentials(new com.sybase.persistence.LoginCredentials(“test@admin”, “test123”)); if (app.getRegistrationStatus() == RegistrationStatus.UNREGISTERED) { app.registerApplication(100); // or call app.registerApplication(); } else { app.startConnection(100); // or call app.startConnection(); } } catch (ApplicationRuntimeException ex) { System.out.println(ex); } catch (ApplicationTimeoutException ex) { System.out.println(ex); } while(app.getConnectionStatus() != ConnectionSatus.CONNECTED || app.getRegistrationStatus() != RegistrationStatus.REGISTED) { try { Thread.sleep(100); } catch (Exception ex) { System.out.println(ex); } } } public class MyApplicationCallback extends com.sybase.mobile.DefaultApplicationCallback { boolean callFlag = false; //override this method to check if need to reregister public void onConnectionStatusChanged(int connectionStatus, int errorCode, String errorMessage) { if (errorCode == 580 && !callFlag) { //this callback will be invoked multiple times when this error occures, but we just call once reregister, so set the //callFlag to be true. callFlag = true; Thread registerThread = new Thread(“reregister”) { public void run() { //do not unregister application, because the application connection info has been deleted from server side. We can //call register application directly. Application.getInstance().registerApplication(); } }.start(); } } }
public void startApplication( ) { Application app = Application.getInstance(); Application.getInstance().setApplicationCallback (new MyApplicationCallback()); try { ConnectionProperties connProperties = app.getConnectionProperties(); connProperties.setServerName(“mega-vm008” ); connProperties.setPortNumber (5001); connProperties.setActivationCode (“100” ); connProperties.setLoginCredentials (new com.sybase.persistence.LoginCredentials(“test@admin”, null)); if (app.getRegistrationStatus() == RegistrationStatus.UNREGISTERED) { app.registerApplication(100); // or call app.registerApplication(); } else { app.startConnection(100); // or call app.startConnection(); } } catch (ApplicationRuntimeException ex) { System.out.println(ex); } catch (ApplicationTimeoutException ex) { System.out.println(ex); } while(app.getConnectionStatus() != ConnectionSatus.CONNECTED || app.getRegistrationStatus() != RegistrationStatus.REGISTED) { try { Thread.sleep(100); } catch (Exception ex) { System.out.println(ex); } } } public class MyApplicationCallback extends com.sybase.mobile.DefaultApplicationCallback { boolean callFlag = false; //override this method to check if need to reregister public void onConnectionStatusChanged(int connectionStatus, int errorCode, String errorMessage) { if (errorCode == 580 && !callFlag) { //this callback will be invoked multiple times when this error occures, but we just call once reregister, so set the //callFlag to be true. callFlag = true; Thread registerThread = new Thread(“reregister”) { public void run() { //do not unregister application, because the application connection info has been deleted from server side. We can //call register application directly. Application.getInstance().registerApplication(); } }.start(); } } }
try { End2end_rdbDB.synchronize(); } catch (Exception ex) { //if meet this error, the server has been restored, we need to recover client database if (ex instanceof com.sybase.persistence.SynchronizeException) { if (((com.sybase.persistence.SynchronizeException) ex).getErrorCode() == com.sybase.persistence.SynchronizeException.SQLE_UPLOAD_FAILED_AT_SERVER) { recoverClientDatabase(); } } } private void recoverClientDatabase() { setRecoveringInPlaceFlag(); //Like save a flag into FileSystem End2end_rdbDB.closeDBConnection(); End2end_rdbDB.deleteDatabase(); cleanRecoveringInPlaceFlag(); }
if(isRecoverFailed()) { recoverClientDatabase(); } else { try { End2end_rdbDB.synchronize(); } catch (Exception ex) { //if meet this error, the server has been restored, we need to recover client database if (ex instanceof com.sybase.persistence.SynchronizeException) { if (((com.sybase.persistence.SynchronizeException) ex).getErrorCode() == com.sybase.persistence.SynchronizeException.SQLE_UPLOAD_FAILED_AT_SERVER) { recoverClientDatabase(); } } } } private void isRecoverFailed() { String dbFile = End2end_rdbDB.getConnectionProfile().getProperty(“databaseFile”); String recoverDbFile = dbFile + ".recover.ulj"; if (new File(recoverDbFile).exists()) { //todo //implement to copy recoverDbFile content to recover dbFile return true; } return false; } private void recoverClientDatabase() { String dbFile = End2end_rdbDB. getConnectionProfile().getProperty(“databaseFile”); String recoverDbFile = dbFile + ".recover.ulj"; //todo //implement to copy dbFile content to recover recoverDbFile //retrieve all the subscriptions from client database GenericList<CustomerWithParamSubscription> _customerWithParamSubscriptions = CustomerWithParam.getSubscriptions(); GenericList<SISSubscription> _sisSubs = SISSubscription.findAll(); GenericList<String> syncedPublication = new GenericList<String>(); // check all the synchronization group, if is synchronized, add to new sync group to synchronize if (End2end_rdbDB.isSynchronized("synchronizationGroup")) { syncedPublication.add("synchronizationGroup "); } //retrieve all local BO from client database GenericList< LocalMbo > localBoList = LocalBo.findAll(); End2end_rdbDB.closeDBConnection(); //subscribe with new database file End2end_rdbDB.deleteDatabase(); End2end_rdbDB.subscribe(); //merge old local BO data to new database for(LocalBo lc : localBoList) { LocalBo localBo = new LocalBo (); localBo.copyAll(lc); localBo.create(); } //add all the subscriptions from old database to new database for (CustomerWithParamSubscription sub : _customerWithParamSubscriptions) { CustomerWithParam.addSubscription(sub); } for (SISSubscription sub : _sisSubs) { ISynchronizationGroup sg = End2end_rdbDB.getSynchronizationGroup(sub.getSyncGroup()); sg.setEnableSIS (sub.getEnable()); sg.save(); } //do sync String syncgroups = ""; for(String pub : syncedPublication) { syncgroups += pub + ","; } syncgroups = syncgroups.substring(0, syncgroups.length() -1 ); End2end_rdbDB.synchronize(syncgroups); //finally delete the backup recover database file new File(recoverDbFile).delete(); }
I. 2013-04-14 14:13:39. <3> The sync sequence ID in the consolidated database: 95bd47691098419cbf8539e8151bcf00; the remote previous sequence ID: 95bd47691098419cbf8539e8151bcf97, and the current sequence ID: 401be536e6e7417fb01b196276ec11c2E. 2013-04-14 14:13:39. <3> [-10400] Invalid sync sequence ID for remote ID 'ed2ae448-a597-4f17-ad72-c6c61a6075a5'
if(isRecoverFailed()) { recoverClientDatabase(); } else { GenericList<String> syncList = new GenericList<String>(); syncList.add(“default”); synchronize(syncList); } private void isRecoverFailed() { String dbFile = End2end_rdbDB.getConnectionProfile().getProperty(“databaseFile”); String recoverDbFile = dbFile + ".recover.ulj"; if (new File(recoverDbFile).exists()) { //todo //implement to copy recoverDbFile content to recover dbFile return true; } return false; } private void synchronize(GenericList<String> syncGroup) { AsyncCallbackHandler callback = new AsyncCallbackHandler(); GenericList<ISynchronizationGroup> sgs = new GenericList<ISynchronizationGroup>(); for(String sg : syncGroup) { sgs.add(End2end_rdbDB .getSynchronizationGroup(sg)); } callback.userContext = System.nanoTime() + ""; End2end_rdbDB.registerCallbackHandler(callback); End2end_rdbDB.beginSynchronize(sgs, callback.userContext); int waitCount = 0; while (!callback.asyncDone()) { if (waitCount++ > maxWaitTime) { throw new Exception("Asyn relay test failed because no response returned from server after waiting for 60 seconds."); } try { Thread.sleep(1000); } catch (Exception e) { } } try { Thread.sleep(4000); } catch (Exception e) { } if (callback.errorMessage != null) { throw new Exception(callback.errorMessage); } callback.userContext = null; } private void recoverClientDatabase() { String dbFile = End2end_rdbDB. getConnectionProfile().getProperty(“databaseFile”); String recoverDbFile = dbFile + ".recover.ulj"; //todo //implement to copy dbFile content to recover recoverDbFile //retrieve all the subscriptions from client database GenericList<CustomerWithParamSubscription> _customerWithParamSubscriptions = CustomerWithParam.getSubscriptions(); GenericList<SISSubscription> _sisSubs = SISSubscription.findAll(); GenericList<String> syncedPublication = new GenericList<String>(); // check all the synchronization group, if is synchronized, add to new sync group to synchronize if (End2end_rdbDB.isSynchronized("synchronizationGroup")) { syncedPublication.add("synchronizationGroup "); } //retrieve all local BO from client database GenericList< LocalMbo > localBoList = LocalBo.findAll(); End2end_rdbDB.closeDBConnection(); //subscribe with new database file End2end_rdbDB.deleteDatabase(); GenericList<String> syncList = new GenericList<String>(); syncList.add(“default”); synchronize(syncList); //merge old local BO data to new database for(LocalBo lc : localBoList) { LocalBo localBo = new LocalBo (); localBo.copyAll(lc); localBo.create(); } //add all the subscriptions from old database to new database for (CustomerWithParamSubscription sub : _customerWithParamSubscriptions) { CustomerWithParam.addSubscription(sub); } for (SISSubscription sub : _sisSubs) { ISynchronizationGroup sg = End2end_rdbDB.getSynchronizationGroup(sub.getSyncGroup()); sg.setEnableSIS(sub.getEnable()); sg.save(); } //do sync synchronize(syncedPublication); //finally delete the backup recover database file new File(recoverDbFile).delete(); } class AsyncCallbackHandler extends DefaultCallbackHandler { private volatile boolean asyncCompleted = false; private volatile boolean asyncUploaded = false; public volatile String userContext = null; public volatile String errorMessage = null; public boolean asyncDone() { return asyncCompleted; } public SynchronizationAction onSynchronize(GenericList<ISynchronizationGroup> groups, SynchronizationContext context) { Exception ex = context.getException(); if (ex instanceof com.sybase.persistence.SynchronizeException) { if (((com.sybase.persistence.SynchronizeException) ex).getErrorCode() == com.sybase.persistence.SynchronizeException.SQLE_UPLOAD_FAILED_AT_SERVER) { recoverClientDatabase(); } } if (context.getStatus() == SynchronizationStatus.ASYNC_REPLAY_UPLOADED) { if (!End2end_rdbDB.isReplayQueueEmpty()) { throw new Exception("need sync is not correct!"); } asyncUploaded = true; } if (context.getStatus() == SynchronizationStatus.ASYNC_REPLAY_COMPLETED) { if (userContext != null && !userContext.equals(context.getUserContext())) //Not for this round { return SynchronizationAction.CANCEL; } userContext = null; End2end_rdbDB.synchronize("so"); if (!asyncUploaded) { errorMessage = "ASYNC_REPLAY_COMPLETED is received without ASYNC_REPLAY_UPLOADED"; } asyncCompleted = true; return SynchronizationAction.CANCEL; } return SynchronizationAction.CONTINUE; } }
If(MyDatabase.isSubscribed()) MyDatabase.unsubscribe(); _rh.waitForMessage("UnsubscribeFailure", "UnsubscribeSuccess"); MyDatabase.subscribe(); _rh.waitForMessage("SubscribeSuccess");
Thread.sleep(10 * 1000); //sleep some time to receive server unsubscribe message when application start up If(!MyDatabase.isSubscribed()) MyDatabase.subscribe();