1 package org.opendaylight.controller.netconf.nettyutil.handler;
3 import com.google.common.base.Preconditions;
4 import com.google.common.cache.CacheBuilder;
5 import com.google.common.cache.CacheLoader;
6 import com.google.common.cache.LoadingCache;
7 import org.openexi.proc.HeaderOptionsOutputType;
8 import org.openexi.proc.common.EXIOptions;
9 import org.openexi.proc.common.EXIOptionsException;
10 import org.openexi.proc.common.GrammarOptions;
11 import org.openexi.proc.grammars.GrammarCache;
12 import org.openexi.sax.EXIReader;
13 import org.openexi.sax.Transmogrifier;
14 import org.openexi.sax.TransmogrifierException;
15 import org.xml.sax.EntityResolver;
16 import org.xml.sax.InputSource;
18 public final class NetconfEXICodec {
20 * NETCONF is XML environment, so the use of EXI cookie is not really needed. Adding it
21 * decreases efficiency of encoding by adding human-readable 4 bytes "EXI$" to the head
22 * of the stream. This is really useful, so let's output it now.
24 private static final boolean OUTPUT_EXI_COOKIE = true;
26 * OpenEXI does not allow us to directly prevent resolution of external entities. In order
27 * to prevent XXE attacks, we reuse a single no-op entity resolver.
29 private static final EntityResolver ENTITY_RESOLVER = new EntityResolver() {
31 public InputSource resolveEntity(final String publicId, final String systemId) {
32 return new InputSource();
37 * Since we have a limited number of options we can have, instantiating a weak cache
38 * will allow us to reuse instances where possible.
40 private static final LoadingCache<Short, GrammarCache> GRAMMAR_CACHES = CacheBuilder.newBuilder().weakValues().build(new CacheLoader<Short, GrammarCache>() {
42 public GrammarCache load(final Short key) {
43 return new GrammarCache(key);
48 * Grammar cache acts as a template and is duplicated by the Transmogrifier and the Reader
49 * before use. It is safe to reuse a single instance.
51 private final GrammarCache exiGrammarCache;
52 private final EXIOptions exiOptions;
54 public NetconfEXICodec(final EXIOptions exiOptions) {
55 this.exiOptions = Preconditions.checkNotNull(exiOptions);
56 this.exiGrammarCache = createGrammarCache(exiOptions);
59 private static GrammarCache createGrammarCache(final EXIOptions exiOptions) {
60 short go = GrammarOptions.DEFAULT_OPTIONS;
61 if (exiOptions.getPreserveComments()) {
62 go = GrammarOptions.addCM(go);
64 if (exiOptions.getPreserveDTD()) {
65 go = GrammarOptions.addDTD(go);
67 if (exiOptions.getPreserveNS()) {
68 go = GrammarOptions.addNS(go);
70 if (exiOptions.getPreservePIs()) {
71 go = GrammarOptions.addPI(go);
74 return GRAMMAR_CACHES.getUnchecked(go);
77 EXIReader getReader() throws EXIOptionsException {
78 final EXIReader r = new EXIReader();
79 r.setPreserveLexicalValues(exiOptions.getPreserveLexicalValues());
80 r.setGrammarCache(exiGrammarCache);
81 r.setEntityResolver(ENTITY_RESOLVER);
85 Transmogrifier getTransmogrifier() throws EXIOptionsException, TransmogrifierException {
86 final Transmogrifier transmogrifier = new Transmogrifier();
87 transmogrifier.setAlignmentType(exiOptions.getAlignmentType());
88 transmogrifier.setBlockSize(exiOptions.getBlockSize());
89 transmogrifier.setGrammarCache(exiGrammarCache);
90 transmogrifier.setOutputCookie(OUTPUT_EXI_COOKIE);
91 transmogrifier.setOutputOptions(HeaderOptionsOutputType.all);
92 transmogrifier.setResolveExternalGeneralEntities(false);
93 return transmogrifier;