📄 enginehelper.java
字号:
/** * Copyright (C) 2003-2004 Funambol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */package sync4j.server.engine;import java.util.*;import java.util.Map;import java.util.logging.Logger;import java.util.logging.Level;import java.sql.Timestamp;import java.security.Principal;import sync4j.framework.core.*;import sync4j.framework.protocol.CommandIdGenerator;import sync4j.framework.engine.SyncOperationStatus;import sync4j.framework.engine.SyncOperation;import sync4j.framework.engine.SyncItemKey;import sync4j.framework.engine.SyncItem;import sync4j.framework.engine.SyncItemImpl;import sync4j.framework.engine.SyncProperty;import sync4j.framework.engine.SyncItemMapping;import sync4j.framework.engine.SyncItemState;import sync4j.framework.engine.SyncConflict;import sync4j.framework.engine.source.*;import sync4j.framework.server.ClientMapping;import sync4j.framework.server.error.MappingException;import sync4j.framework.logging.Sync4jLogger;import sync4j.server.engine.Sync4jOperationStatus;/** * This class collects helper methods used by the engine. * * @author Stefano Fornari */public class EngineHelper { public static Logger log = Sync4jLogger.getLogger(Sync4jEngine.LOG_NAME); /** * Status are grouped as per their original commands and status code so * that the number of returned status will be minimal. This is done by * generating an hash map where the key is a couple * <i>(original command id, status code)</i> and the value is the * <i>StatusCommand</i> object. Finally the hash map is iterated over to * return the status commands array.<br> * Note that Sync4jOperationStatus's command is meaningful only for items * that derive from a SyncML command. If the associated command is null, * there is no need to generate a status. Of course, also if the command was * flagged as "noResponse" no status is required. * * @param operationStatus the status object returned as the result of * commands execution * @param msgId the message id * @param the command id generator * * @return a <i>StatusCommand[]</i> array containing the status to return * to the client */ public static Status[] generateStatusCommands(SyncOperationStatus[] operationStatus, String msgId , CommandIdGenerator idGenerator ) { HashMap statusMap = new HashMap(); ArrayList itemList = null; StatusKey key = null; ModificationCommand cmd = null; for (int i=0; i < operationStatus.length ; ++i) { cmd = ((Sync4jOperationStatus)operationStatus[i]).getCmd(); if ((cmd == null) || (cmd.isNoResp())) { continue; // skip the operation status } // // Check if a status command has been already created for the given // original command and status code. If it is found, than a new // item is added to the existing ones, otherwise a new item list // is created // key = new StatusKey( cmd, operationStatus[i].getStatusCode() ); itemList = (ArrayList)statusMap.get(key); if (itemList == null){ itemList = new ArrayList(); statusMap.put(key, itemList); } itemList.add(operationStatus[i].getOperation().getSyncItemA().getKey()); } // // Now we can loop over the map and create a list of status commands. // ArrayList ret = new ArrayList(); Item[] items = null; Iterator i = statusMap.keySet().iterator(); while(i.hasNext()) { key = (StatusKey)i.next(); itemList = (ArrayList)statusMap.get(key); items = new Item[itemList.size()]; for (int j = 0; j<items.length; ++j) { items[j] = new Item( null, new Source(((SyncItemKey)itemList.get(j)).getKeyAsString()), null, null, false ); } ret.add( new Status( idGenerator.next() , msgId , key.cmd.getCmdID().getCmdID() , key.cmd.getName() , null , (items.length == 1) ? new SourceRef(items[0].getSource()) : null , null /* credential */ , null /* challenge */ , new Data(key.statusCode) , (items.length > 1) ? items : new Item[0] ) ); } return (Status[])ret.toArray(new Status[ret.size()]); } /** * Translates an array of <i>SyncOperation</i> objects to an array of * <i>(Add,Delete,Replace)Command</i> objects. Only client side operations * are translated. * * @param clientMapping the associated existing client mapping * @param operations the operations to be translated * @param sourceName the corresponding source name * @param idGenerator the ID generator for command ids * * @return the sync4j commands corresponding to <i>operations</i> */ public static ItemizedCommand[] operationsToCommands(ClientMapping clientMapping, SyncOperation[] operations , String sourceName , CommandIdGenerator idGenerator ) { ArrayList commands = new ArrayList(); SyncItem item = null; for (int i=0; ((operations != null) && (i<operations.length)); ++i) { if (log.isLoggable(Level.FINEST)) { log.finest( "Converting the operation\n" + operations[i] + "\nfor the source " + sourceName ); } item = operations[i].getSyncItemB(); // this is the server-side item if (operations[i].isAOperation() && (item != null)) { char op = operations[i].getOperation(); // // Should not translates a NOP operation // if (op == SyncOperation.NOP) { continue; } commands.add(operationToCommand(clientMapping, operations[i], idGenerator)); } } return (ItemizedCommand[])commands.toArray(new ItemizedCommand[0]); } /** * Translates a <i>SyncOperation</i> object to a <i>(Add,Delete,Replace)Command</i> * object. * * @param clientMapping the item ids mapping * @param operation the operation to be translated * @param command id generator to use to create command ids * * @return the sync4j command corresponding to <i>operation</i> */ public static ItemizedCommand operationToCommand(ClientMapping clientMapping, SyncOperation operation , CommandIdGenerator idGenerator ) { ItemizedCommand cmd = null; if (idGenerator == null) { log.finest("idGenerator is null. Cannot continue"); throw new NullPointerException("idGenerator cannot be null!"); } char op = operation.getOperation(); // // The item key must reflect the value known by the client agent. It // thus must be adjusted using the client mapping, but if the // operation is an addition. In this case the client key is generated // by the engine (the client will provide the right key with a // subsequent Map command). // String itemKey = operation.getSyncItemB().getKey().getKeyAsString(); if (op != SyncOperation.NEW && op != SyncOperation.CONFLICT) { itemKey = clientMapping.getMappedValueForGuid(itemKey); } assert (itemKey != null); // // The following rules apply: // - If the operation is an addition, Targer MUST NOT be included. // - If the operation is not an addition, Source MUST NOT be included. // - If the operation is not a deletion, Data element MUST be used to carry data ifself // // TO DO: noResponse, credential // Meta m = null; if (op == SyncOperation.NEW) { m = new Meta(); m.setType(operation.getSyncItemB().getSyncSource().getType()); cmd = new Add( idGenerator.next(), false , null , m , // meta new Item[] { SyncItemHelper.toItem( itemKey , operation.getSyncItemA(), false , true , true ) } ); } else if (op == SyncOperation.DELETE) { cmd = new Delete( idGenerator.next(), false , false , false , null , null , // meta new Item[] { SyncItemHelper.toItem( itemKey, operation.getSyncItemA(), true, false, false ) } ); } else if (op == SyncOperation.UPDATE) { m = new Meta(); m.setType(operation.getSyncItemB().getSyncSource().getType()); cmd = new Replace( idGenerator.next(), false , null , m , new Item[] { SyncItemHelper.toItem( itemKey, operation.getSyncItemA(), true, false, true ) } ); } else if (op == SyncOperation.CONFLICT) { // // Server wins! // // TO DO: implement other conflict resolution policies // if (operation.getSyncItemB().getSyncSource() != null) { m = new Meta(); m.setType(operation.getSyncItemB().getSyncSource().getType()); } if (operation.getSyncItemB().getState() == SyncItemState.NOT_EXISTING) { itemKey = operation.getSyncItemA().getKey().getKeyAsString(); } //View the state of syncItemB and then say to client which the operation //to do in order to resolve the conflict char s = operation.getSyncItemB().getState(); switch (s) { case SyncItemState.UPDATED: case SyncItemState.SYNCHRONIZED: cmd = new Replace( idGenerator.next(), false , null , m , new Item[] { SyncItemHelper.toItem( itemKey, operation.getSyncItemA(), true, false, true ) } ); break; case SyncItemState.DELETED: case SyncItemState.NOT_EXISTING: cmd = new Delete( idGenerator.next(), false , false , false , null , null , // meta new Item[] { SyncItemHelper.toItem( itemKey, operation.getSyncItemA(), true, false, false ) } ); break; case SyncItemState.NEW: cmd = new Add( idGenerator.next(), false , null , m , // meta new Item[] { SyncItemHelper.toItem( itemKey , operation.getSyncItemA(), false , true , true ) } ); break; } } return cmd; } /** * Converts an array of <i>Item</i> objects belonging to the same * <i>SyncSource</i> into an array of <i>SyncItem</i> objects. * <p> * The <i>Item</i>s created are enriched with an additional property * called as in SyncItemHelper.PROPERTY_COMMAND, which is used to bound the * newly created object with the original command. * * @param syncSource the <i>SyncSource</i> items belong to - NOT NULL * @param items the <i>Item</i> objects * @param state the state of the item as one of the values defined in * <i>SyncItemState</i> * @param the timestamp to assign to the last even on this item * * * @return an array of <i>SyncItem</i> objects */ public static SyncItem[] itemsToSyncItems(ClientMapping clientMapping, SyncSource syncSource ,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -