@Override public Cell postMutationBeforeWAL(ObserverContext ctx, MutationType opType, Map opAttributes, List oldCells, Cell newCell) throws IOException { List tags = Lists.newArrayList(); // First, accumulate tags other than ACLs from old cells for this mutation // for the new KV List oldKVs = KeyValueUtil.ensureKeyValues(oldCells); for (KeyValue kv: oldKVs) { Iterator tagIterator = kv.tagsIterator(); while (tagIterator.hasNext()) { Tag tag = tagIterator.next(); if (tag.getType() != AccessControlLists.ACL_TAG_TYPE) { if (LOG.isTraceEnabled()) { LOG.trace("Adding tag from " + kv + ": type " + tag.getType() + " length " + tag.getValue().length); } tags.add(tag); } } } // Do we have an ACL on the operation? byte[] aclBytes = opAttributes.get(AccessControlConstants.OP_ATTRIBUTE_ACL); if (aclBytes != null) { // Yes, use it tags.add(new Tag(AccessControlLists.ACL_TAG_TYPE, aclBytes)); } else { // No, collected the union of previous cell permissions into one tag for // the new cell ListMultimap newPerms = ArrayListMultimap.create(); for (KeyValue kv: oldKVs) { Iterator tagIterator = kv.tagsIterator(); while (tagIterator.hasNext()) { Tag tag = tagIterator.next(); if (tag.getType() == AccessControlLists.ACL_TAG_TYPE) { ListMultimap kvPerms = ProtobufUtil.toUsersAndPermissions( AccessControlProtos.UsersAndPermissions.newBuilder().mergeFrom( tag.getBuffer(), tag.getTagOffset(), tag.getTagLength()).build()); if (LOG.isTraceEnabled()) { LOG.trace("Adding ACL from " + kv + ": " + kvPerms); } newPerms.putAll(kvPerms); } } } // TODO: Optimize any List with size > 1 into one Permission. // We should make Permission internally into a bitset. // At least we won't need to deserialize multiple ACL tags on the same // cell after this. tags.add(new Tag(AccessControlLists.ACL_TAG_TYPE, ProtobufUtil.toUsersAndPermissions(newPerms).toByteArray())); } // If we have no tags to add, just return if (tags.isEmpty()) { return newCell; } // We need to create another KV, unfortunately, because the current new KV // has no space for tags KeyValue newKv = KeyValueUtil.ensureKeyValue(newCell); byte[] bytes = newKv.getBuffer(); KeyValue rewriteKv = new KeyValue(bytes, newKv.getRowOffset(), newKv.getRowLength(), bytes, newKv.getFamilyOffset(), newKv.getFamilyLength(), bytes, newKv.getQualifierOffset(), newKv.getQualifierLength(), newKv.getTimestamp(), KeyValue.Type.codeToType(newKv.getTypeByte()), bytes, newKv.getValueOffset(), newKv.getValueLength(), tags); // Preserve mvcc data rewriteKv.setMvccVersion(newKv.getMvccVersion()); return rewriteKv; }