/* * Copyright 2011 Future Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.confdb.file; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.krakenapps.api.CollectionTypeHint; import org.krakenapps.codec.EncodingRule; import org.krakenapps.confdb.CommitLog; import org.krakenapps.confdb.CommitOp; import org.krakenapps.confdb.ConfigChange; /** * database level change set log * * @author xeraph * */ class ChangeLog implements CommitLog { private long rev; private Date created = new Date(); private String committer; private String message; private int manifestId; @CollectionTypeHint(ConfigChange.class) private List<ConfigChange> changeset = new ArrayList<ConfigChange>(); @Override public long getRev() { return rev; } public void setRev(long rev) { this.rev = rev; } @Override public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } @Override public String getCommitter() { return committer; } public void setCommitter(String committer) { this.committer = committer; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public List<ConfigChange> getChangeSet() { return changeset; } public void setChangeSet(List<ConfigChange> changeset) { this.changeset = changeset; } public int getManifestId() { return manifestId; } public void setManifestId(int manifestId) { this.manifestId = manifestId; } public byte[] serialize() { Map<String, Object> m = new HashMap<String, Object>(); // "rev" is doc id of revision log (do not require serialize) m.put("created", created); m.put("committer", committer); m.put("msg", message); m.put("manifest_id", manifestId); m.put("col_names", serializeCollectionNames()); m.put("changeset", serializeChangeset()); ByteBuffer bb = ByteBuffer.allocate(EncodingRule.lengthOf(m)); EncodingRule.encode(bb, m); return bb.array(); } private List<Object> serializeChangeset() { List<Object> l = new ArrayList<Object>(changeset.size()); for (ConfigChange c : changeset) { l.add(new Object[] { c.getOperation().getCode(), c.getColId(), c.getDocId() }); } return l; } private List<Object> serializeCollectionNames() { Map<String, Integer> m = new HashMap<String, Integer>(); for (ConfigChange c : changeset) m.put(c.getColName(), c.getColId()); List<Object> cols = new ArrayList<Object>(); for (String name : m.keySet()) { cols.add(new Object[] { m.get(name), name }); } return cols; } private static Map<Integer, String> parseCollectionNames(Object[] l) { Map<Integer, String> m = new HashMap<Integer, String>(); if (l == null) return m; for (Object o : l) { Object[] arr = (Object[]) o; m.put((Integer) arr[0], (String) arr[1]); } return m; } /** * get manifest id from change log binary. manual parsing for speed-up */ public static int getManifest(byte[] b) { ByteBuffer bb = ByteBuffer.wrap(b); bb.get(); // type (9) EncodingRule.decodeRawNumber(bb); // skip map length part // enumerate keys of map while (true) { // parse map key String s = EncodingRule.decodeString(bb); if (s.equals("manifest_id")) { return EncodingRule.decodeInt(bb); } else { // parse map value bb.get(); long l = EncodingRule.decodeRawNumber(bb); bb.position((int) (bb.position() + l)); } } } public static ChangeLog deserialize(byte[] b) { ByteBuffer bb = ByteBuffer.wrap(b); Map<String, Object> m = EncodingRule.decodeMap(bb); ChangeLog c = new ChangeLog(); c.setCreated((Date) m.get("created")); c.setCommitter((String) m.get("committer")); c.setMessage((String) m.get("msg")); c.setManifestId((Integer) m.get("manifest_id")); Map<Integer, String> colNames = parseCollectionNames((Object[]) m.get("col_names")); c.setChangeSet(parseConfigChanges((Object[]) m.get("changeset"), colNames)); return c; } private static List<ConfigChange> parseConfigChanges(Object[] list, Map<Integer, String> colNames) { List<ConfigChange> l = new ArrayList<ConfigChange>(list.length); for (Object o : list) { if (o instanceof Map) { // legacy format support @SuppressWarnings("unchecked") Map<String, Object> m = (Map<String, Object>) o; ConfigChange c = new ConfigChange(); c.setOperation(CommitOp.valueOf((String) m.get("operation"))); c.setColId((Integer) m.get("col_id")); c.setColName(colNames.get(c.getColId())); c.setDocId((Integer) m.get("doc_id")); l.add(c); } else { Object[] arr = (Object[]) o; ConfigChange c = new ConfigChange(); c.setOperation(CommitOp.parse((Integer) arr[0])); c.setColId((Integer) arr[1]); c.setColName(colNames.get(c.getColId())); c.setDocId((Integer) arr[2]); l.add(c); } } return l; } @Override public String toString() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ"); StringBuilder sb = new StringBuilder(); int i = 0; for (ConfigChange c : changeset) { if (i++ != 0) sb.append(", "); sb.append(c.toString()); } String createdDate = ", date="; if (created != null) createdDate += dateFormat.format(created); return "rev=" + rev + createdDate + ", committer=" + committer + ", msg=" + message + ", changeset=[" + sb.toString() + "]"; } }