View Javadoc

1   package org.apache.helix.manager.zk;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  
26  import org.I0Itec.zkclient.exception.ZkMarshallingError;
27  import org.I0Itec.zkclient.serialize.ZkSerializer;
28  
29  public class ChainedPathZkSerializer implements PathBasedZkSerializer
30  {
31  
32    public static class Builder
33    {
34      private final ZkSerializer _defaultSerializer;
35      private List<ChainItem> _items = new ArrayList<ChainItem>();
36  
37      private Builder(ZkSerializer defaultSerializer)
38      {
39        _defaultSerializer = defaultSerializer;
40      }
41  
42      /**
43       * Add a serializing strategy for the given path prefix
44       * The most specific path will triumph over a more generic (shorter)
45       * one regardless of the ordering of the calls.
46       */
47      public Builder serialize(String path, ZkSerializer withSerializer)
48      {
49        _items.add(new ChainItem(normalize(path), withSerializer));
50        return this;
51      }
52      
53      /**
54       * Builds the serializer with the given strategies and default serializer.
55       */
56      public ChainedPathZkSerializer build() {
57        return new ChainedPathZkSerializer(_defaultSerializer, _items);
58      }
59    }
60    
61    /**
62     * Create a builder that will use the given serializer by default
63     * if no other strategy is given to solve the path in question.
64     */
65    public static Builder builder(ZkSerializer defaultSerializer) 
66    {
67      return new Builder(defaultSerializer);
68    }
69  
70    private final List<ChainItem> _items;
71    private final ZkSerializer _defaultSerializer;
72  
73    private ChainedPathZkSerializer(ZkSerializer defaultSerializer, List<ChainItem> items)
74    {
75      _items = items;
76      // sort by longest paths first
77      // if two items would match one would be prefix of the other
78      // and the longest must be more specific
79      Collections.sort(_items);
80      _defaultSerializer = defaultSerializer;
81    }
82  
83    @Override
84    public byte[] serialize(Object data, String path) throws ZkMarshallingError
85    {
86      for (ChainItem item : _items)
87      {
88        if (item.matches(path)) return item._serializer.serialize(data);
89      }
90      return _defaultSerializer.serialize(data);
91    }
92  
93    @Override
94    public Object deserialize(byte[] bytes, String path)
95        throws ZkMarshallingError
96    {
97      for (ChainItem item : _items)
98      {
99        if (item.matches(path)) return item._serializer.deserialize(bytes);
100     }
101     return _defaultSerializer.deserialize(bytes);
102   }
103 
104   private static class ChainItem implements Comparable<ChainItem>
105   {
106     final String _path;
107     final ZkSerializer _serializer;
108 
109     ChainItem(String path, ZkSerializer serializer)
110     {
111       _path = path;
112       _serializer = serializer;
113     }
114 
115     boolean matches(String path)
116     {
117       if (_path.equals(path))
118       {
119         return true;
120       } 
121       else if (path.length() > _path.length())
122       {
123         if (path.startsWith(_path) && path.charAt(_path.length()) == '/') 
124         {
125           return true;
126         }
127       }
128       return false;
129     }
130 
131     @Override
132     public int compareTo(ChainItem o)
133     {
134       return o._path.length() - _path.length();
135     }
136   }
137   
138   private static String normalize(String path) {
139     if (!path.startsWith("/")) {
140       // ensure leading slash
141       path = "/" + path;
142     }
143     if (path.endsWith("/")) {
144       // remove trailing slash
145       path = path.substring(0, path.length()-1);
146     }
147     return path;
148   }
149 
150 }