📄 syncmanager.java
字号:
/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2003 - 2007 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.syncml.spds;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import com.funambol.util.Base64;
import com.funambol.util.StringUtil;
import com.funambol.util.Log;
import com.funambol.syncml.protocol.SyncML;
import com.funambol.syncml.protocol.SyncMLStatus;
import com.funambol.syncml.protocol.SyncMLCommand;
import com.funambol.syncml.protocol.SyncFilter;
import com.funambol.util.XmlUtil;
import com.funambol.util.XmlException;
import com.funambol.util.BasicSyncListener;
import com.funambol.util.SyncListener;
import com.funambol.util.ChunkedString;
/**
* The SyncManager is the engine of the synchronization process on the
* client library. It initializes the sync, checks the server responses
* and communicate with the SyncSource, which is the client-specific
* source of data.
* A client developer must prepare a SyncConfig to istantiate a
* SyncManager, and then can sync its sources calling the sync()
* method.
*/
public class SyncManager {
//------------------------------------------------------------- Private data
/* Fast sync sending add state*/
private static final int STATE_SENDING_ADD = 1;
/* Fast sync sending update state*/
private static final int STATE_SENDING_REPLACE = 2;
/* Fast sync sending delete state*/
private static final int STATE_SENDING_DELETE = 3;
/* Fast sync modification complete state*/
private static final int STATE_MODIFICATION_COMPLETED = 4;
/* SyncManager configuration*/
private SyncConfig config;
/* SyncSource to sync*/
private SyncSource source;
/* Device ID taken from DeviceConfig*/
private String deviceId;
/* Max SyncML Message Size taken from DeviceConfig*/
private int maxMsgSize;
/**
* A flag indicating if the client has to prepare the <DevInf> part of the
* initialization SyncML message containing the device capabilities. It can
* be set to <code>true</code> in two falls:
*
* a) the <code>serverUrl</code> isn't on the list of the already
* connected servers
*
* b) the device configuration is changed
*/
private boolean sendDevInf = false;
/**
* A flag indicating if the client has to add the device capabilities to the
* modification message as content of a <Results> element. This occurs when
* the server has sent a <Get> command request, sollicitating items of type
* './devinf12'
*/
private boolean addDevInfResults = false;
/**
* String containing the last Url of the server the client was connected to
*/
private String lastServerUrl;
/**
* The value of the <CmdID> element of <Get>, to be used building the
* <Results> command
*/
private String cmdIDget = null;
/**
* The value of the <MsgID> element of the message in which <Get> is, to be
* used building the <Results> command
*/
private String msgIDget = null;
// state used for fast sync
int state;
// The alerts sent by server, indexed by source name, instantiated in
// checkServerAlerts
private Hashtable serverAlerts;
// The alert code for the current source (i.e. the actual sync mode
// eventually modified by ther server
private int alertCode;
// Server URL modified with session id.
private String serverUrl;
// FIXME: ca be avoided?
private String login = null;
private String sessionID = null;
/**
* This member stores the LUID/GUID mapping for the items added
* to the current source during the sync.
*/
private Hashtable mappings = null;
/**
* This member stores the Status commands to send back to the server
* in the next message. It is modified at each item received,
* and is cleared after the status are sent.
*/
private Vector statusList = null;
/**
* This member is used to store the current message ID.
* It is sent to the server in the MsgID tag.
*/
private int msgID = 0;
/**
* This member is used to store the current command ID.
* It is sent to the server in the CmdID tag.
*/
private int cmdID = 0;
/**
* A single HttpTransportAgent for all the operations
* performed in this Sync Manager
*/
private HttpTransportAgent transportAgent;
private static final int PROTOCOL_OVERHEAD = 3072;
/**
* This member is used to indicate if the SyncManager is busy, that is
* if a sync is on going (SyncManager supports only one synchronization
* at a time, and requests are queued in the synchronized method sync
*/
private boolean busy;
/**
* Synchronization listener
*/
private SyncListener listener;
/**
* Unique instance of a BasicSyncListener which is used when the user does
* not set up a listener in the SyncSource. In order to avoid the creation
* of multiple instances of this class we use this static variable
*/
private static SyncListener basicListener = null;
//------------------------------------------------------------- Constructors
/**
* SyncManager constructor
*
* @param conf is the configuration data filled by the client
*
*/
public SyncManager(SyncConfig conf) {
this.config = conf;
this.login = conf.userName + ":" + conf.password;
this.source = null;
// Cache device info
this.deviceId = config.deviceConfig.devID;
this.maxMsgSize = config.deviceConfig.maxMsgSize;
this.state = 0;
this.serverAlerts = null;
this.alertCode = 0;
// mapping table
this.mappings = null;
this.busy = false;
// status commands
statusList = null;
transportAgent =
new HttpTransportAgent(
config.syncUrl,
config.userAgent,
"UTF-8",
conf.compress, conf.forceCookies);
}
//----------------------------------------------------------- Public methods
/**
* Synchronizes synchronization source, using the preferred sync
* mode defined for that SyncSource.
*
* @param source the SyncSource to synchronize
*
* @throws SyncException
* If an error occurs during synchronization
*
*/
public void sync(SyncSource source) throws SyncException {
sync(source, source.getSyncMode());
}
/**
* Synchronizes synchronization source
*
* @param source the SyncSource to synchronize
* @param syncMode the sync mode
* @throws SyncException
* If an error occurs during synchronization
*/
public synchronized void sync(SyncSource src, int syncMode)
throws SyncException {
busy = true;
// Get the SyncListener associated to the source.
// If it does not exist we use the dummy listener
listener = src.getListener();
if (listener == null) {
if (basicListener == null) {
listener = new BasicSyncListener();
basicListener = listener;
} else {
listener = basicListener;
}
}
// Notifies the listener that a new sync is about to start
listener.startSession();
if (syncMode == SyncML.ALERT_CODE_NONE) {
Log.info("Source not active.");
listener.endSession(SyncSource.STATUS_SUCCESS);
return;
}
try {
String response = null;
// Set source attribute
this.source = src;
// Set initial state
nextState(STATE_SENDING_ADD);
//Set NEXT Anchor referring to current timestamp
this.source.setNextAnchor(System.currentTimeMillis());
this.sessionID = String.valueOf(System.currentTimeMillis());
this.serverUrl = config.syncUrl;
//deciding if the device capabilities have to be sent
if (isNewServerUrl(serverUrl)) {
setFlagSendDevInf();
}
// ================================================================
// Initialization phase
// ================================================================
Log.info("Sending init message");
listener.startConnecting();
//Format request message to be sent to the server
String initMsg = prepareInizializationMessage(syncMode);
Log.debug(initMsg);
response = postRequest(initMsg);
initMsg = null;
Log.info("Response received");
Log.debug("Response: " + response);
// TODO: today the SyncSource does not need to process this data.
// When we support large object we may want to change this
listener.dataReceived(transportAgent.getResponseDate(),
response.length());
ChunkedString chunkedResp = new ChunkedString(response);
// Check server response (can throws exception and break the sync)
checkStatus(chunkedResp, SyncML.TAG_SYNCHDR);
checkStatus(chunkedResp, SyncML.TAG_ALERT);
// client interpretes server alerts and store them into "serverAlerts"
checkServerAlerts(chunkedResp);
// save the alert code for the current source
String name = source.getName();
Log.debug(name);
alertCode = getSourceAlertCode(name);
Log.info("Alert code: " + alertCode);
Log.info("Initialization succesfully completed");
listener.endConnecting(alertCode);
// if the server has required device capabilities in the response, these
// are added within the next client request in the <Results> method
addDevInfResults = isGetCommandFromServer(chunkedResp);
// Get the server URL with the session ID
try {
serverUrl = XmlUtil.getTagValue(chunkedResp, "RespURI").toString();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -