This project has retired. For details please refer to its Attic page.
Source code
001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.reef.tang.implementation.avro;
020
021import org.apache.avro.file.DataFileReader;
022import org.apache.avro.file.DataFileWriter;
023import org.apache.avro.io.*;
024import org.apache.avro.specific.SpecificDatumReader;
025import org.apache.avro.specific.SpecificDatumWriter;
026import org.apache.reef.tang.ClassHierarchy;
027import org.apache.reef.tang.ClassHierarchySerializer;
028import org.apache.reef.tang.types.*;
029
030import javax.inject.Inject;
031import java.io.*;
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.List;
035
036/**
037 * Serialize and Deserialize ClassHierarchy to and from AvroClassHierarchy.
038 * This class is stateless and is therefore safe to reuse.
039 */
040public final class AvroClassHierarchySerializer implements ClassHierarchySerializer {
041
042  /**
043   * The Charset used for the JSON encoding.
044   */
045  private static final String JSON_CHARSET = "ISO-8859-1";
046
047  @Inject
048  public AvroClassHierarchySerializer() {
049  }
050
051  /**
052   * Serialize the ClassHierarchy into the AvroNode.
053   * @param ch ClassHierarchy to serialize
054   * @return a Avro node having class hierarchy
055   */
056  public AvroNode toAvro(final ClassHierarchy ch) {
057    return newAvroNode(ch.getNamespace());
058  }
059
060  /**
061   * Deserialize the ClassHierarchy from the AvroNode.
062   * @param n AvroNode to deserialize
063   * @return a class hierarchy
064   */
065  public ClassHierarchy fromAvro(final AvroNode n) {
066    return new AvroClassHierarchy(n);
067  }
068
069  private AvroNode newAvroNode(final Node n) {
070    final List<AvroNode> children = new ArrayList<>();
071    for (final Node child : n.getChildren()) {
072      children.add(newAvroNode(child));
073    }
074    if (n instanceof ClassNode) {
075      final ClassNode<?> cn = (ClassNode<?>) n;
076      final ConstructorDef<?>[] injectable = cn.getInjectableConstructors();
077      final ConstructorDef<?>[] all = cn.getAllConstructors();
078      final List<ConstructorDef<?>> others = new ArrayList<>(Arrays.asList(all));
079      others.removeAll(Arrays.asList(injectable));
080
081      final List<AvroConstructorDef> injectableConstructors = new ArrayList<>();
082      for (final ConstructorDef<?> inj : injectable) {
083        injectableConstructors.add(newConstructorDef(inj));
084      }
085      final List<AvroConstructorDef> otherConstructors = new ArrayList<>();
086      for (final ConstructorDef<?> other : others) {
087        otherConstructors.add(newConstructorDef(other));
088      }
089      final List<CharSequence> implFullNames = new ArrayList<>();
090      for (final ClassNode<?> impl : cn.getKnownImplementations()) {
091        implFullNames.add(impl.getFullName());
092      }
093      return newClassNode(n.getName(), n.getFullName(), cn.isInjectionCandidate(), cn.isExternalConstructor(),
094              cn.isUnit(), injectableConstructors, otherConstructors, implFullNames, cn.getDefaultImplementation(),
095              children);
096    } else if (n instanceof NamedParameterNode) {
097      final NamedParameterNode<?> np = (NamedParameterNode<?>) n;
098      final List<CharSequence> defaultInstances = new ArrayList<>();
099      for (final CharSequence defaultInstance : np.getDefaultInstanceAsStrings()) {
100        defaultInstances.add(defaultInstance);
101      }
102      return newNamedParameterNode(np.getName(), np.getFullName(), np.getSimpleArgName(), np.getFullArgName(),
103              np.isSet(), np.isList(), np.getDocumentation(), np.getShortName(), defaultInstances, children);
104    } else if (n instanceof PackageNode) {
105      return newPackageNode(n.getName(), n.getFullName(), children);
106    } else {
107      throw new IllegalStateException("Encountered unknown type of Node: " + n);
108    }
109  }
110
111  private AvroNode newClassNode(final String name,
112                                       final String fullName,
113                                       final boolean isInjectionCandidate,
114                                       final boolean isExternalConstructor,
115                                       final boolean isUnit,
116                                       final List<AvroConstructorDef> injectableConstructors,
117                                       final List<AvroConstructorDef> otherConstructors,
118                                       final List<CharSequence> implFullNames,
119                                       final String defaultImplementation,
120                                       final List<AvroNode> children) {
121    return AvroNode.newBuilder()
122            .setName(name)
123            .setFullName(fullName)
124            .setClassNode(AvroClassNode.newBuilder()
125                    .setIsInjectionCandidate(isInjectionCandidate)
126                    .setIsExternalConstructor(isExternalConstructor)
127                    .setIsUnit(isUnit)
128                    .setInjectableConstructors(injectableConstructors)
129                    .setOtherConstructors(otherConstructors)
130                    .setImplFullNames(implFullNames)
131                    .setDefaultImplementation(defaultImplementation)
132                    .build())
133            .setChildren(children).build();
134  }
135
136  private AvroNode newNamedParameterNode(final String name,
137                                                final String fullName,
138                                                final String simpleArgClassName,
139                                                final String fullArgClassName,
140                                                final boolean isSet,
141                                                final boolean isList,
142                                                final String documentation,
143                                                final String shortName,
144                                                final List<CharSequence> instanceDefault,
145                                                final List<AvroNode> children) {
146
147    return AvroNode.newBuilder()
148            .setName(name)
149            .setFullName(fullName)
150            .setNamedParameterNode(AvroNamedParameterNode.newBuilder()
151                    .setSimpleArgClassName(simpleArgClassName)
152                    .setFullArgClassName(fullArgClassName)
153                    .setIsSet(isSet)
154                    .setIsList(isList)
155                    .setDocumentation(documentation)
156                    .setShortName(shortName)
157                    .setInstanceDefault(instanceDefault)
158                    .build())
159            .setChildren(children).build();
160  }
161
162  private AvroNode newPackageNode(final String name,
163                                         final String fullName,
164                                         final List<AvroNode> children) {
165    return AvroNode.newBuilder()
166            .setPackageNode(AvroPackageNode.newBuilder().build())
167            .setName(name).setFullName(fullName).setChildren(children).build();
168  }
169
170  private AvroConstructorArg newConstructorArg(final String fullArgClassName,
171                                                      final String namedParameterName,
172                                                      final boolean isFuture) {
173    return AvroConstructorArg.newBuilder()
174            .setFullArgClassName(fullArgClassName)
175            .setIsInjectionFuture(isFuture)
176            .setNamedParameterName(namedParameterName).build();
177  }
178
179  private AvroConstructorDef newConstructorDef(final ConstructorDef<?> def) {
180    final List<AvroConstructorArg> args = new ArrayList<>();
181    for (final ConstructorArg arg : def.getArgs()) {
182      args.add(newConstructorArg(arg.getType(), arg.getNamedParameterName(), arg.isInjectionFuture()));
183    }
184    return AvroConstructorDef.newBuilder()
185            .setFullClassName(def.getClassName())
186            .setConstructorArgs(args).build();
187  }
188
189  @Override
190  public void toFile(final ClassHierarchy classHierarchy, final File file) throws IOException {
191    final AvroNode avroNode = toAvro(classHierarchy);
192    final DatumWriter<AvroNode> avroNodeWriter = new SpecificDatumWriter<>(AvroNode.class);
193    try (final DataFileWriter<AvroNode> dataFileWriter = new DataFileWriter<>(avroNodeWriter)) {
194      dataFileWriter.create(avroNode.getSchema(), file);
195      dataFileWriter.append(avroNode);
196    }
197  }
198
199  @Override
200  public byte[] toByteArray(final ClassHierarchy classHierarchy) throws IOException {
201    final DatumWriter<AvroNode> requestWriter = new SpecificDatumWriter<>(AvroNode.class);
202    try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) {
203      final BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(out, null);
204      requestWriter.write(toAvro(classHierarchy), encoder);
205      encoder.flush();
206      return out.toByteArray();
207    }
208  }
209
210  @Override
211  public String toString(final ClassHierarchy classHierarchy) throws IOException {
212    final DatumWriter<AvroNode> classHierarchyWriter = new SpecificDatumWriter<>(AvroNode.class);
213    try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) {
214      final JsonEncoder encoder = EncoderFactory.get().jsonEncoder(AvroNode.SCHEMA$, out);
215      classHierarchyWriter.write(toAvro(classHierarchy), encoder);
216      encoder.flush();
217      out.flush();
218      return out.toString(JSON_CHARSET);
219    }
220  }
221
222  @Override
223  public void toTextFile(final ClassHierarchy classHierarchy, final File file) throws IOException {
224    try (final Writer w = new FileWriter(file)) {
225      w.write(this.toString(classHierarchy));
226    }
227  }
228
229  @Override
230  public ClassHierarchy fromFile(final File file) throws IOException {
231    final AvroNode avroNode;
232    try (final DataFileReader<AvroNode> dataFileReader =
233                 new DataFileReader<>(file, new SpecificDatumReader<>(AvroNode.class))) {
234      avroNode = dataFileReader.next();
235    }
236    return fromAvro(avroNode);
237  }
238
239  @Override
240  public ClassHierarchy fromByteArray(final byte[] theBytes) throws IOException {
241    final BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(theBytes, null);
242    final SpecificDatumReader<AvroNode> reader = new SpecificDatumReader<>(AvroNode.class);
243    return fromAvro(reader.read(null, decoder));
244  }
245
246  @Override
247  public ClassHierarchy fromString(final String theString) throws IOException {
248    final JsonDecoder decoder = DecoderFactory.get().jsonDecoder(AvroNode.getClassSchema(), theString);
249    final SpecificDatumReader<AvroNode> reader = new SpecificDatumReader<>(AvroNode.class);
250    return fromAvro(reader.read(null, decoder));
251  }
252
253  @Override
254  public ClassHierarchy fromTextFile(final File file) throws IOException {
255    final StringBuilder stringBuilder = new StringBuilder();
256    try (final BufferedReader reader = new BufferedReader(new FileReader(file))) {
257      String line = reader.readLine();
258      while (line != null) {
259        stringBuilder.append(line);
260        line = reader.readLine();
261      }
262    }
263    return fromString(stringBuilder.toString());
264  }
265}