View Javadoc

1   /*
2    * Copyright 2010-2011 Ning, Inc.
3    *
4    * Ning licenses this file to you under the Apache License, version 2.0
5    * (the "License"); you may not use this file except in compliance with the
6    * License.  You may obtain a copy of the License at:
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package com.ning.metrics.goodwill.access;
18  
19  import com.google.common.collect.ImmutableMap;
20  import org.codehaus.jackson.JsonGenerationException;
21  import org.codehaus.jackson.annotate.JsonCreator;
22  import org.codehaus.jackson.annotate.JsonProperty;
23  import org.codehaus.jackson.annotate.JsonValue;
24  import org.codehaus.jackson.map.ObjectMapper;
25  
26  import java.io.ByteArrayOutputStream;
27  import java.io.IOException;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Comparator;
31  import java.util.HashMap;
32  import java.util.List;
33  
34  /**
35   * Describe a Schema in Goodwill.
36   * This is basically a union of a Schema and extra metadata for the Sink.
37   *
38   * @see com.ning.metrics.serialization.schema.Schema
39   */
40  public class GoodwillSchema
41  {
42      private static final ObjectMapper mapper = new ObjectMapper();
43  
44      private final String name;
45      private String sinkAddInfo;
46      private final HashMap<Short, GoodwillSchemaField> thriftItems = new HashMap<Short, GoodwillSchemaField>();
47  
48      public static final String JSON_THRIFT_TYPE_NAME = "name";
49      public static final String JSON_THRIFT_TYPE_SCHEMA = "schema";
50      public static final String JSON_THRIFT_TYPE_SINK_ADD_INFO = "sinkAddInfo";
51  
52      /**
53       * Jackson constructor
54       * <p/>
55       * {
56       * "sinkAddInfo": null,
57       * "name": "hello",
58       * "schema": [
59       * {
60       * "name": "my hello attribute",
61       * "type": "string",
62       * "position": 1,
63       * "description": "awesome attribute",
64       * "sql": {
65       * "type": "nvarchar",
66       * "length": null,
67       * "scale": null,
68       * "precision": null
69       * }
70       * },
71       * {
72       * "name": "dsfdfsfds",
73       * "type": "bool",
74       * "position": 2,
75       * "description": "dfsfdsfds",
76       * "sql": {
77       * "type": "boolean",
78       * "length": null,
79       * "scale": null,
80       * "precision": null
81       * }
82       * },
83       * {
84       * "name": "wer",
85       * "type": "double",
86       * "position": 3,
87       * "description": "wer",
88       * "sql": {
89       * "type": "numeric",
90       * "length": null,
91       * "scale": 12,
92       * "precision": 42
93       * }
94       * }
95       * ]
96       * }
97       *
98       * @param name        Schema name
99       * @param items       List of fields
100      * @param sinkAddInfo extra information for the Sink
101      */
102     @JsonCreator
103     public GoodwillSchema(
104         @JsonProperty(JSON_THRIFT_TYPE_NAME) final String name,
105         @JsonProperty(JSON_THRIFT_TYPE_SCHEMA) final List<GoodwillSchemaField> items,
106         @JsonProperty(JSON_THRIFT_TYPE_SINK_ADD_INFO) final String sinkAddInfo
107     )
108     {
109         this(name, items);
110         setSinkAddInfo(sinkAddInfo);
111     }
112 
113     /**
114      * Manual constructor, typically used by Goodwill stores.
115      *
116      * @param name  Schema name
117      * @param items List of fields
118      */
119     public GoodwillSchema(final String name, final List<GoodwillSchemaField> items)
120     {
121         this.name = name;
122         for (final GoodwillSchemaField field : items) {
123             addThriftField(field);
124         }
125     }
126 
127     public static GoodwillSchema decode(final String thriftJson) throws IOException
128     {
129         return mapper.readValue(thriftJson, GoodwillSchema.class);
130     }
131 
132     @JsonValue
133     @SuppressWarnings({"unchecked"})
134     public ImmutableMap toMap()
135     {
136         return new ImmutableMap.Builder()
137             .put(JSON_THRIFT_TYPE_NAME, getName())
138             .put(JSON_THRIFT_TYPE_SCHEMA, getSchema())
139             .put(JSON_THRIFT_TYPE_SINK_ADD_INFO, sinkAddInfo == null ? "" : sinkAddInfo)
140             .build();
141     }
142 
143     /**
144      * Add a field in the Thrift. The code does not enforce sanity w.r.t. field positions.
145      *
146      * @param goodwillSchemaField field to add
147      */
148     public void addThriftField(final GoodwillSchemaField goodwillSchemaField)
149     {
150         thriftItems.put(goodwillSchemaField.getId(), goodwillSchemaField);
151     }
152 
153     public String getName()
154     {
155         return name;
156     }
157 
158     /**
159      * Get the schema as a collection of fields.
160      * We guarantee the ordering by field id.
161      *
162      * @return the sorted collection of fields
163      */
164     public ArrayList<GoodwillSchemaField> getSchema()
165     {
166         final ArrayList<GoodwillSchemaField> items = new ArrayList<GoodwillSchemaField>(thriftItems.values());
167 
168         Collections.sort(items, new Comparator<GoodwillSchemaField>()
169         {
170             @Override
171             public int compare(final GoodwillSchemaField left, final GoodwillSchemaField right)
172             {
173                 return Short.valueOf(left.getId()).compareTo(right.getId());
174             }
175         });
176 
177         return items;
178     }
179 
180     public void setSinkAddInfo(final String sinkAddInfo)
181     {
182         this.sinkAddInfo = sinkAddInfo;
183     }
184 
185     /**
186      * Given a position, return the field at that position.
187      *
188      * @param i position in the Thrift (start with 1)
189      * @return the GoodwillSchemaField object
190      */
191     public GoodwillSchemaField getFieldByPosition(final short i)
192     {
193         return thriftItems.get(i);
194     }
195 
196     /**
197      * Given a name, return the field matching the name.
198      *
199      * @param name GoodwillSchemaField name
200      * @return the GoodwillSchemaField object
201      */
202     public GoodwillSchemaField getFieldByName(final String name)
203     {
204         for (final GoodwillSchemaField field : thriftItems.values()) {
205             if (field.getName().equals(name)) {
206                 return field;
207             }
208         }
209 
210         return null;
211     }
212 
213     @Override
214     public String toString()
215     {
216         try {
217             return mapper.writeValueAsString(this);
218         }
219         catch (JsonGenerationException e) {
220             return "GoodwillSchema{" +
221                 JSON_THRIFT_TYPE_NAME + "='" + getName() + '\'' +
222                 ", thriftItems=" + getSchema() +
223                 '}';
224         }
225         catch (IOException e) {
226             return "GoodwillSchema{" +
227                 JSON_THRIFT_TYPE_NAME + "='" + getName() + '\'' +
228                 ", thriftItems=" + getSchema() +
229                 '}';
230         }
231     }
232 
233     public byte[] toJSONBytes() throws IOException
234     {
235         return mapper.writeValueAsBytes(this);
236     }
237 
238     /**
239      * @deprecated Use 'toJSONBytes()' instead
240      */
241     @Deprecated
242     public ByteArrayOutputStream toJSON() throws IOException
243     {
244         // silly, but 
245         final byte[] bytes = toJSONBytes();
246         final ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
247         out.write(bytes);
248         return out;
249     }
250 }