Merge "Bug 2517: Catch RuntimeExceptions thrown from the DCL in DataChangeListener"
[controller.git] / opendaylight / netconf / netconf-netty-util / src / main / java / org / opendaylight / controller / netconf / nettyutil / handler / NetconfEXICodec.java
1 package org.opendaylight.controller.netconf.nettyutil.handler;
2
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;
17
18 public final class NetconfEXICodec {
19     /**
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.
23      */
24     private static final boolean OUTPUT_EXI_COOKIE = true;
25     /**
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.
28      */
29     private static final EntityResolver ENTITY_RESOLVER = new EntityResolver() {
30         @Override
31         public InputSource resolveEntity(final String publicId, final String systemId) {
32             return new InputSource();
33         }
34     };
35
36     /**
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.
39      */
40     private static final LoadingCache<Short, GrammarCache> GRAMMAR_CACHES = CacheBuilder.newBuilder().weakValues().build(new CacheLoader<Short, GrammarCache>() {
41         @Override
42         public GrammarCache load(final Short key) {
43             return new GrammarCache(key);
44         }
45     });
46
47     /**
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.
50      */
51     private final GrammarCache exiGrammarCache;
52     private final EXIOptions exiOptions;
53
54     public NetconfEXICodec(final EXIOptions exiOptions) {
55         this.exiOptions = Preconditions.checkNotNull(exiOptions);
56         this.exiGrammarCache = createGrammarCache(exiOptions);
57     }
58
59     private static GrammarCache createGrammarCache(final EXIOptions exiOptions) {
60         short go = GrammarOptions.DEFAULT_OPTIONS;
61         if (exiOptions.getPreserveComments()) {
62             go = GrammarOptions.addCM(go);
63         }
64         if (exiOptions.getPreserveDTD()) {
65             go = GrammarOptions.addDTD(go);
66         }
67         if (exiOptions.getPreserveNS()) {
68             go = GrammarOptions.addNS(go);
69         }
70         if (exiOptions.getPreservePIs()) {
71             go = GrammarOptions.addPI(go);
72         }
73
74         return GRAMMAR_CACHES.getUnchecked(go);
75     }
76
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);
82         return r;
83     }
84
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;
94     }
95 }