public boolean addAt(int index, Object e) { if (index < 0) index = size(); while (index > size()) { if (!add(Converter.convert(null, type))) return false; } if (type != null) e = Converter.convert(e, type); super.add(index, e); return true; }
private static NCube checkForConflicts( ApplicationID appId, Map<String, Map> errors, String message, NCubeInfoDto info, NCubeInfoDto head, boolean reverse) { Map<String, Object> map = new LinkedHashMap<>(); map.put("message", message); map.put("sha1", info.sha1); map.put("headSha1", head != null ? head.sha1 : null); try { if (head != null) { long branchCubeId = (long) Converter.convert(info.id, long.class); long headCubeId = (long) Converter.convert(head.id, long.class); NCube branchCube = getPersister().loadCubeById(branchCubeId); NCube headCube = getPersister().loadCubeById(headCubeId); if (info.headSha1 != null) { NCube baseCube = getPersister().loadCubeBySha1(appId, info.name, info.headSha1); Map delta1 = baseCube.getDelta(branchCube); Map delta2 = baseCube.getDelta(headCube); if (NCube.areDeltaSetsCompatible(delta1, delta2)) { if (reverse) { headCube.mergeCellChangeSet(delta1); return headCube; } else { branchCube.mergeCellChangeSet(delta2); return branchCube; } } } List<Delta> diff = branchCube.getDeltaDescription(headCube); if (diff.size() > 0) { map.put("diff", diff); } else { return branchCube; } } else { map.put("diff", null); } } catch (Exception e) { map.put("diff", e.getMessage()); } errors.put(info.name, map); return null; }
public Object set(int index, Object e) { Object old = get(index); if (index < 0 || index >= size()) add(index, e); else { if (type != null) e = Converter.convert(e, type); super.set(index, e); } return old; }
static NCube ensureLoaded(Object value) { if (value instanceof NCube) { return (NCube) value; } if (value instanceof NCubeInfoDto) { // Lazy load cube (make sure to apply any advices to it) NCubeInfoDto dto = (NCubeInfoDto) value; long id = (long) Converter.convert(dto.id, long.class); return prepareCube(getPersister().loadCubeById(id)); } throw new IllegalStateException("Failed to retrieve cube from cache, value: " + value); }
@SuppressWarnings({"unchecked", "rawtypes"}) private void truncate(Trace<?> dest, Trace<?> orig) throws IOException { long min_time = minTime * orig.ticsPerSecond(); long max_time = maxTime * orig.ticsPerSecond(); Converter truncater; if (orig.isStateful()) { truncater = new StatefulSubtraceConverter( (StatefulTrace<?, ?>) dest, (StatefulTrace<?, ?>) orig, min_time, max_time); } else { truncater = new SubtraceConverter(dest, orig, min_time, max_time); } truncater.convert(); }
/** * Update a branch from the HEAD. Changes from the HEAD are merged into the supplied branch. If * the merge cannot be done perfectly, an exception is thrown indicating the cubes that are in * conflict. */ public static Map<String, Object> updateBranch(ApplicationID appId, String username) { validateAppId(appId); appId.validateBranchIsNotHead(); appId.validateStatusIsNotRelease(); ApplicationID headAppId = appId.asHead(); Map<String, Object> options = new HashMap<>(); options.put(SEARCH_ACTIVE_RECORDS_ONLY, false); List<NCubeInfoDto> records = search(appId, null, null, options); Map<String, NCubeInfoDto> branchRecordMap = new CaseInsensitiveMap<>(); for (NCubeInfoDto info : records) { branchRecordMap.put(info.name, info); } List<NCubeInfoDto> updates = new ArrayList<>(); List<NCubeInfoDto> dtosMerged = new ArrayList<>(); Map<String, Map> conflicts = new CaseInsensitiveMap<>(); List<NCubeInfoDto> headRecords = search(headAppId, null, null, options); for (NCubeInfoDto head : headRecords) { NCubeInfoDto info = branchRecordMap.get(head.name); if (info == null) { // HEAD has cube that branch does not have updates.add(head); continue; } long infoRev = (long) Converter.convert(info.revision, long.class); long headRev = (long) Converter.convert(head.revision, long.class); boolean activeStatusMatches = (infoRev < 0) == (headRev < 0); // Did branch change? if (!info.isChanged()) { // No change on branch if (!activeStatusMatches || !StringUtilities.equalsIgnoreCase( info.headSha1, head.sha1)) { // 1. The active/deleted statuses don't match, or // 2. HEAD has different SHA1 but branch cube did not change, safe to update branch (fast // forward) // In both cases, the cube was marked NOT changed in the branch, so safe to update. updates.add(head); } } else if (StringUtilities.equalsIgnoreCase( info.sha1, head.sha1)) { // If branch is 'changed' but has same SHA-1 as head, then see if branch // needs Fast-Forward if (!StringUtilities.equalsIgnoreCase(info.headSha1, head.sha1)) { // Fast-Forward branch // Update HEAD SHA-1 on branch directly (no need to insert) getPersister() .updateBranchCubeHeadSha1((Long) Converter.convert(info.id, Long.class), head.sha1); } } else { if (!StringUtilities.equalsIgnoreCase( info.headSha1, head.sha1)) { // Cube is different than HEAD, AND it is not based on same HEAD cube, but // it could be merge-able. String message = "Cube was changed in both branch and HEAD"; NCube cube = checkForConflicts(appId, conflicts, message, info, head, true); if (cube != null) { NCubeInfoDto mergedDto = getPersister().commitMergedCubeToBranch(appId, cube, head.sha1, username); dtosMerged.add(mergedDto); } } } } List<NCubeInfoDto> finalUpdates = new ArrayList<>(updates.size()); Object[] ids = new Object[updates.size()]; int i = 0; for (NCubeInfoDto dto : updates) { ids[i++] = dto.id; } finalUpdates.addAll(getPersister().pullToBranch(appId, ids, username)); clearCache(appId); Map<String, Object> ret = new LinkedHashMap<>(); ret.put(BRANCH_UPDATES, finalUpdates); ret.put(BRANCH_MERGES, dtosMerged); ret.put(BRANCH_CONFLICTS, conflicts); return ret; }
/** * Commit the passed in changed cube records identified by NCubeInfoDtos. * * @return array of NCubeInfoDtos that are to be committed. */ public static List<NCubeInfoDto> commitBranch( ApplicationID appId, Object[] infoDtos, String username) { validateAppId(appId); appId.validateBranchIsNotHead(); appId.validateStatusIsNotRelease(); ApplicationID headAppId = appId.asHead(); Map<String, NCubeInfoDto> headMap = new TreeMap<>(); Map<String, Object> options = new HashMap<>(); options.put(SEARCH_ACTIVE_RECORDS_ONLY, false); List<NCubeInfoDto> headInfo = search(headAppId, null, null, options); // build map of head objects for reference. for (NCubeInfoDto info : headInfo) { headMap.put(info.name, info); } List<NCubeInfoDto> dtosToUpdate = new ArrayList<>(infoDtos.length); List<NCubeInfoDto> dtosMerged = new ArrayList<>(); Map<String, Map> errors = new LinkedHashMap<>(); for (Object dto : infoDtos) { NCubeInfoDto branchCubeInfo = (NCubeInfoDto) dto; if (!branchCubeInfo.isChanged()) { continue; } if (branchCubeInfo.sha1 == null) { branchCubeInfo.sha1 = ""; } // All changes go through here. NCubeInfoDto headCubeInfo = headMap.get(branchCubeInfo.name); long infoRev = (long) Converter.convert(branchCubeInfo.revision, long.class); if (headCubeInfo == null) { // No matching head cube, CREATE case if (infoRev >= 0) { // Only create if the cube in the branch is active (revision number not // negative) dtosToUpdate.add(branchCubeInfo); } } else if (StringUtilities.equalsIgnoreCase( branchCubeInfo.headSha1, headCubeInfo .sha1)) { // HEAD cube has not changed (at least in terms of SHA-1 it could have it's // revision sign changed) if (StringUtilities.equalsIgnoreCase( branchCubeInfo.sha1, branchCubeInfo .headSha1)) { // Cubes are same, but active status could be opposite (delete or // restore case) long headRev = (long) Converter.convert(headCubeInfo.revision, long.class); if ((infoRev < 0) != (headRev < 0)) { dtosToUpdate.add(branchCubeInfo); } } else { // Regular update case (branch updated cube that was not touched in HEAD) dtosToUpdate.add(branchCubeInfo); } } else if (StringUtilities.equalsIgnoreCase( branchCubeInfo.sha1, headCubeInfo .sha1)) { // Branch headSha1 does not match HEAD sha1, but it's SHA-1 matches the HEAD // SHA-1. // This means that the branch cube and HEAD cube are identical, but the HEAD was // different when the branch was created. dtosToUpdate.add(branchCubeInfo); } else { String msg; if (branchCubeInfo.headSha1 == null) { msg = ". A cube with the same name was added to HEAD since your branch was created."; } else { msg = ". The cube changed since your last update branch."; } String message = "Conflict merging " + branchCubeInfo.name + msg; NCube mergedCube = checkForConflicts(appId, errors, message, branchCubeInfo, headCubeInfo, false); if (mergedCube != null) { NCubeInfoDto mergedDto = getPersister().commitMergedCubeToHead(appId, mergedCube, username); dtosMerged.add(mergedDto); } } } if (!errors.isEmpty()) { throw new BranchMergeException( errors.size() + " merge conflict(s) committing branch. Update your branch and retry commit.", errors); } List<NCubeInfoDto> committedCubes = new ArrayList<>(dtosToUpdate.size()); Object[] ids = new Object[dtosToUpdate.size()]; int i = 0; for (NCubeInfoDto dto : dtosToUpdate) { ids[i++] = dto.id; } committedCubes.addAll(getPersister().commitCubes(appId, ids, username)); committedCubes.addAll(dtosMerged); clearCache(appId); clearCache(headAppId); broadcast(appId); return committedCubes; }
/** * Get List<NCubeInfoDto> of n-cube record DTOs for the given ApplicationID (branch only). If * using For any cube record loaded, for which there is no entry in the app's cube cache, an entry * is added mapping the cube name to the cube record (NCubeInfoDto). This will be replaced by an * NCube if more than the name is required. one (1) character. This is universal whether using a * SQL perister or Mongo persister. */ public static List<NCubeInfoDto> getBranchChangesFromDatabase(ApplicationID appId) { validateAppId(appId); if (appId.getBranch().equals(ApplicationID.HEAD)) { throw new IllegalArgumentException("Cannot get branch changes from HEAD"); } ApplicationID headAppId = appId.asHead(); Map<String, NCubeInfoDto> headMap = new TreeMap<>(); Map<String, Object> searchChangedRecordOptions = new HashMap<>(); searchChangedRecordOptions.put(SEARCH_CHANGED_RECORDS_ONLY, true); List<NCubeInfoDto> branchList = search(appId, null, null, searchChangedRecordOptions); Map<String, Object> options = new HashMap<>(); options.put(SEARCH_ACTIVE_RECORDS_ONLY, false); List<NCubeInfoDto> headList = search(headAppId, null, null, options); List<NCubeInfoDto> list = new ArrayList<>(); // build map of head objects for reference. for (NCubeInfoDto info : headList) { headMap.put(info.name, info); } // Loop through changed (added, deleted, created, restored, updated) records for (NCubeInfoDto info : branchList) { long revision = (long) Converter.convert(info.revision, long.class); NCubeInfoDto head = headMap.get(info.name); if (head == null) { if (revision >= 0) { info.changeType = ChangeType.CREATED.getCode(); list.add(info); } } else if (info.headSha1 == null) { // we created this guy locally // someone added this one to the head already info.changeType = ChangeType.CONFLICT.getCode(); list.add(info); } else { if (StringUtilities.equalsIgnoreCase(info.headSha1, head.sha1)) { if (StringUtilities.equalsIgnoreCase(info.sha1, info.headSha1)) { // only net change could be revision deleted or restored. check head. long headRev = Long.parseLong(head.revision); if (headRev < 0 != revision < 0) { if (revision < 0) { info.changeType = ChangeType.DELETED.getCode(); } else { info.changeType = ChangeType.RESTORED.getCode(); } list.add(info); } } else { info.changeType = ChangeType.UPDATED.getCode(); list.add(info); } } else { info.changeType = ChangeType.CONFLICT.getCode(); list.add(info); } } } cacheCubes(appId, list); return list; }