blob: 6975ea1a54db8d4d977ad5ddd9d014e374fdcd2c [file] [log] [blame]
package org.apache.helix.manager.zk;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.ZkSerializer;
public class ChainedPathZkSerializer implements PathBasedZkSerializer {
public static class Builder {
private final ZkSerializer _defaultSerializer;
private List<ChainItem> _items = new ArrayList<ChainItem>();
private Builder(ZkSerializer defaultSerializer) {
_defaultSerializer = defaultSerializer;
}
/**
* Add a serializing strategy for the given path prefix
* The most specific path will triumph over a more generic (shorter)
* one regardless of the ordering of the calls.
*/
public Builder serialize(String path, ZkSerializer withSerializer) {
_items.add(new ChainItem(normalize(path), withSerializer));
return this;
}
/**
* Builds the serializer with the given strategies and default serializer.
*/
public ChainedPathZkSerializer build() {
return new ChainedPathZkSerializer(_defaultSerializer, _items);
}
}
/**
* Create a builder that will use the given serializer by default
* if no other strategy is given to solve the path in question.
*/
public static Builder builder(ZkSerializer defaultSerializer) {
return new Builder(defaultSerializer);
}
private final List<ChainItem> _items;
private final ZkSerializer _defaultSerializer;
private ChainedPathZkSerializer(ZkSerializer defaultSerializer, List<ChainItem> items) {
_items = items;
// sort by longest paths first
// if two items would match one would be prefix of the other
// and the longest must be more specific
Collections.sort(_items);
_defaultSerializer = defaultSerializer;
}
@Override
public byte[] serialize(Object data, String path) throws ZkMarshallingError {
for (ChainItem item : _items) {
if (item.matches(path))
return item._serializer.serialize(data);
}
return _defaultSerializer.serialize(data);
}
@Override
public Object deserialize(byte[] bytes, String path) throws ZkMarshallingError {
for (ChainItem item : _items) {
if (item.matches(path))
return item._serializer.deserialize(bytes);
}
return _defaultSerializer.deserialize(bytes);
}
private static class ChainItem implements Comparable<ChainItem> {
final String _path;
final ZkSerializer _serializer;
ChainItem(String path, ZkSerializer serializer) {
_path = path;
_serializer = serializer;
}
boolean matches(String path) {
if (_path.equals(path)) {
return true;
} else if (path.length() > _path.length()) {
if (path.startsWith(_path) && path.charAt(_path.length()) == '/') {
return true;
}
}
return false;
}
@Override
public int compareTo(ChainItem o) {
return o._path.length() - _path.length();
}
}
private static String normalize(String path) {
if (!path.startsWith("/")) {
// ensure leading slash
path = "/" + path;
}
if (path.endsWith("/")) {
// remove trailing slash
path = path.substring(0, path.length() - 1);
}
return path;
}
}