/** * INTERNAL: Get the appropriate attribute value from the object and put it in the appropriate * field of the database row. Loop through the reference objects and extract the primary keys and * put them in the vector of "nested" rows. */ public void writeFromObjectIntoRowWithChangeRecord( ChangeRecord changeRecord, AbstractRecord row, AbstractSession session) { if (isForeignKeyRelationship()) { Object object = ((ObjectChangeSet) changeRecord.getOwner()).getUnitOfWorkClone(); this.writeFromObjectIntoRow(object, row, session); } else { super.writeFromObjectIntoRowWithChangeRecord(changeRecord, row, session); } }
private void synchronize(ChangeRecord record) throws IOException { try { switch (record.getOperation()) { case LOCAL_INSERT: { // Get remote id for the parent folder File local = DriveUtils.absolutePath(record.getLocalFile()); String remoteParent = stub.storage().localToRemote().get(DriveUtils.relativePath(local.getParentFile())); if (!StringUtils.isEmpty(remoteParent) && !stub.storage().localToRemote().containsKey(record.getLocalFile())) { // Ignore insert request that doesn't have a parent or // already have a mapping. // The insert operation will be accomplished by the topmost // folder String remoteId = stub.transmit().upload(remoteParent, local); record.setRemoteFileId(remoteId); } break; } case LOCAL_DELETE: { if (!StringUtils.isEmpty(record.getRemoteFileId())) { stub.transmit().delete(record.getRemoteFileId()); } else { logger.warn( "Local deletion has no remote reference, make sure the storage is correct."); } break; } case LOCAL_CHANGE: { if (!StringUtils.isEmpty(record.getRemoteFileId())) { File local = DriveUtils.absolutePath(record.getLocalFile()); stub.transmit().update(record.getRemoteFileId(), local); } else { logger.warn( "Local change has no remote reference, make sure the storage is correct."); logger.warn("Trying to insert the new record"); // Modify it to a local insert record.setOperation(Operation.LOCAL_INSERT); synchronize(record); } break; } case LOCAL_RENAME: { if (!StringUtils.isEmpty(record.getRemoteFileId())) { stub.transmit().rename(record.getRemoteFileId(), (String) record.getContext()[0]); } else { logger.warn( "Local rename has no remote reference, make sure the storage is correct."); logger.warn("Trying to insert the new record"); // Modify it to be a local insert record.setOperation(Operation.LOCAL_INSERT); synchronize(record); } break; } case REMOTE_INSERT: { if (stub.storage().remoteToLocal().containsKey(record.getRemoteFileId())) { // This file/folder already had been created break; } com.google.api.services.drive.model.File file = record.getContext(0); // Depth search of parent that has a local root List<ParentReference> path = new ArrayList<ParentReference>(); File local = pathToLocal(record.getRemoteFileId(), path); if (null == local) { // Check whether this file is trashed if (file.getLabels().getTrashed()) { return; } if (file.getShared()) { return; } else { throw new IllegalArgumentException("Non-trash file has no known parent"); } } File parent = local; for (ParentReference node : path) { stub.transmit().download(node.getId(), parent); parent = DriveUtils.absolutePath(stub.storage().remoteToLocal().get(node.getId())); } stub.transmit().download(record.getRemoteFileId(), parent); record.setLocalFile(stub.storage().remoteToLocal().get(record.getRemoteFileId())); if (StringUtils.isEmpty(record.getLocalFile())) { break; } break; } case REMOTE_DELETE: { if (StringUtils.isEmpty(record.getLocalFile())) { // No local file, remote file should be a trashed one. if (logger.isDebugEnabled()) { logger.debug( "No corresponding local file for deletion. Remote file may be trashed"); } break; } File localFile = DriveUtils.absolutePath(record.getLocalFile()); String localParent = DriveUtils.relativePath(localFile.getParentFile()); // Preserve the original context record.setContext(new Object[] {record.getContext(0), localParent}); deleteLocalFile(localFile); if (localFile.exists()) { if (logger.isDebugEnabled()) { logger.debug( MessageFormat.format( "Failed to delete file {0}. File may have been deleted.", record.getLocalFile())); } } stub.storage().localToRemote().remove(record.getLocalFile()); stub.storage().remoteToLocal().remove(record.getRemoteFileId()); break; } case REMOTE_CHANGE: { File local = DriveUtils.absolutePath(record.getLocalFile()); File localParent = local.getParentFile(); com.google.api.services.drive.model.File remote = record.getContext(0); if (!remote.getTitle().equals(local.getName())) { local.delete(); stub.storage().localToRemote().remove(record.getLocalFile()); stub.storage().remoteToLocal().remove(record.getRemoteFileId()); } stub.transmit().download(record.getRemoteFileId(), localParent); break; } case REMOTE_RENAME: { File local = DriveUtils.absolutePath(record.getLocalFile()); com.google.api.services.drive.model.File remote = record.getContext(0); String remoteName = remote.getTitle(); File newName = new File(local.getParentFile().getAbsolutePath() + File.separator + remoteName); String relNewName = DriveUtils.relativePath(newName); record.setContext(new Object[] {record.getContext()[0], relNewName}); local.renameTo(newName); stub.storage() .remoteToLocal() .put(record.getRemoteFileId(), DriveUtils.relativePath(newName)); stub.storage().localToRemote().remove(DriveUtils.relativePath(local)); stub.storage() .localToRemote() .put(DriveUtils.relativePath(newName), record.getRemoteFileId()); break; } } } catch (Exception e) { logger.warn(MessageFormat.format("Exception on Change {0}. Retry later", record), e); FailedRecord fr = new FailedRecord(record); if (e instanceof GoogleJsonResponseException) { GoogleJsonResponseException gjre = (GoogleJsonResponseException) e; fr.setError(String.valueOf(gjre.getDetails().getCode())); } else { fr.setError(e.getMessage()); } stub.storage().failedLog().add(fr); } }
@Override public void synchronize() throws IOException { if (logger.isDebugEnabled()) { logger.debug("Synchronizing..."); } // Detect remote change List<ChangeRecord> remoteChanges = remoteChange(); // Detect local change Snapshot standard = stub.storage().get(StorageService.SNAPSHOT); Snapshot current = stub.snapshot().make(); List<ChangeRecord> localChanges = compare(standard, current); /* * First upload then download. Local change has higher priority than * remote because remote change can be restored while local cannot. * Recorded uploaded file so later no need to download */ Map<String, String> localUploaded = new HashMap<String, String>(); for (ChangeRecord localChange : localChanges) { synchronize(localChange); localUploaded.put(localChange.getLocalFile(), localChange.getRemoteFileId()); } PriorityQueue<ChangeRecord> orderedRemoteChanges = new PriorityQueue<ChangeRecord>( 50, new Comparator<ChangeRecord>() { @Override public int compare(ChangeRecord o1, ChangeRecord o2) { if (o1.getOperation().ordinal() < o2.getOperation().ordinal()) { return -1; } else if (o1.getOperation().ordinal() > o2.getOperation().ordinal()) { return 1; } else { if (o1.getLocalFile() == null) return -1; if (o2.getLocalFile() == null) return 1; return o1.getLocalFile().compareTo(o2.getLocalFile()); } } }); for (ChangeRecord remoteChange : remoteChanges) { if (!localUploaded.containsKey(remoteChange.getLocalFile())) { synchronize(remoteChange); if (remoteChange.getOperation() == Operation.REMOTE_INSERT) { remoteChange.setLocalFile( stub.storage().remoteToLocal().get(remoteChange.getRemoteFileId())); } orderedRemoteChanges.add(remoteChange); } } while (!orderedRemoteChanges.isEmpty()) { addToSnapshot(current, orderedRemoteChanges.poll()); } // Retry until all the errors are processed while (!stub.storage().failedLog().isEmpty()) { FailedRecord fr = stub.storage().failedLog().remove(0); // Filter, modify and discard fr = correct(fr); if (fr != null) { synchronize(fr); } } stub.storage().put(StorageService.SNAPSHOT, current); }
private void addToSnapshot(Snapshot root, ChangeRecord remoteChange) { switch (remoteChange.getOperation()) { case REMOTE_INSERT: String localName = stub.storage().remoteToLocal().get(remoteChange.getRemoteFileId()); if (StringUtils.isEmpty(localName)) { // Insert failed return; } File localFile = DriveUtils.absolutePath(localName); String parent = DriveUtils.relativePath(localFile.getParentFile()); if (root.getName().equals(parent)) { for (Snapshot sn : root.getChildren()) { if (sn.getName().equals(localName)) return; } Snapshot sn = stub.snapshot().make(localFile); root.addChild(sn); } else { for (Snapshot sn : root.getChildren()) { if (parent.startsWith(sn.getName())) { addToSnapshot(sn, remoteChange); return; } } // TODO the operations are not in sequence. // Didn't find? logger.warn( MessageFormat.format("Remote insert cannot find local parent {0}", remoteChange)); // throw new IllegalArgumentException(); } break; case REMOTE_CHANGE: { String fileName = remoteChange.getLocalFile(); if (fileName.equals(root.getName())) { com.google.api.services.drive.model.File remoteFile = remoteChange.getContext(0); root.setMd5Checksum(remoteFile.getMd5Checksum()); } else { for (Snapshot sn : root.getChildren()) { if (fileName.startsWith(sn.getName())) { addToSnapshot(sn, remoteChange); return; } } // Didn't find? logger.error( MessageFormat.format( "Local root doesn't contain this file {0}. Should be an error.", remoteChange)); // throw new IllegalArgumentException(); } break; } case REMOTE_RENAME: { String fileName = remoteChange.getLocalFile(); if (root.getName().startsWith(fileName)) { String newName = remoteChange.getContext(1); root.setName(root.getName().replaceFirst(fileName, newName)); for (Snapshot sn : root.getChildren()) { addToSnapshot(sn, remoteChange); } } else { for (Snapshot sn : root.getChildren()) { if (fileName.startsWith(sn.getName())) { addToSnapshot(sn, remoteChange); return; } } // Didn't find? logger.error( MessageFormat.format( "Remote rename cannot find local corresponding {0}, should be an error", remoteChange)); // throw new IllegalArgumentException(); } break; } case REMOTE_DELETE: String fileName = remoteChange.getLocalFile(); if (StringUtils.isEmpty(fileName)) { return; } for (int i = 0; i < root.getChildren().size(); i++) { Snapshot sn = root.getChildren().get(i); if (fileName.equals(sn.getName())) { root.getChildren().remove(sn); return; } if (fileName.startsWith(sn.getName())) { addToSnapshot(sn, remoteChange); return; } } // Didn't find? logger.warn( MessageFormat.format( "Remote change cannot find local corresponding {0}, possibly caused by deleting of parent folder", remoteChange)); // This means the remote change is out of date break; default: throw new IllegalArgumentException(); } }