BUG-5280: optimize identifier serialization format
[controller.git] / opendaylight / md-sal / cds-access-api / src / main / java / org / opendaylight / controller / cluster / access / concepts / FrontendType.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.cluster.access.concepts;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.Preconditions;
13 import com.google.common.base.Strings;
14 import com.google.common.base.Verify;
15 import java.io.DataInput;
16 import java.io.DataOutput;
17 import java.io.Externalizable;
18 import java.io.IOException;
19 import java.io.ObjectInput;
20 import java.io.ObjectOutput;
21 import java.nio.charset.StandardCharsets;
22 import java.util.regex.Pattern;
23 import org.opendaylight.yangtools.concepts.Identifier;
24
25 /**
26  * An {@link Identifier} identifying a data store frontend type, which is able to access the data store backend.
27  * Frontend implementations need to define this identifier so that multiple clients existing on a member node can be
28  * discerned.
29  *
30  * @author Robert Varga
31  */
32 @Beta
33 public final class FrontendType implements Comparable<FrontendType>, Identifier, WritableObject {
34     private static final class Proxy implements Externalizable {
35         private static final long serialVersionUID = 1L;
36         private byte[] serialized;
37
38         public Proxy() {
39             // For Externalizable
40         }
41
42         Proxy(final byte[] serialized) {
43             this.serialized = Preconditions.checkNotNull(serialized);
44         }
45
46         @Override
47         public void writeExternal(final ObjectOutput out) throws IOException {
48             out.writeInt(serialized.length);
49             out.write(serialized);
50         }
51
52         @Override
53         public void readExternal(final ObjectInput in) throws IOException {
54             serialized = new byte[in.readInt()];
55             in.readFully(serialized);
56         }
57
58         private Object readResolve() {
59             // TODO: consider caching instances here
60             return new FrontendType(new String(serialized, StandardCharsets.UTF_8), serialized);
61         }
62     }
63
64     private static final Pattern SIMPLE_STRING_PATTERN = Pattern.compile("^[a-zA-Z-_.*+:=,!~';]+$");
65     private static final long serialVersionUID = 1L;
66     private final String name;
67     private volatile byte[] serialized;
68
69     private FrontendType(final String name) {
70         this.name = Preconditions.checkNotNull(name);
71     }
72
73     FrontendType(final String name, final byte[] serialized) {
74         this(name);
75         this.serialized = Verify.verifyNotNull(serialized);
76     }
77
78     /**
79      * Return a {@link FrontendType} corresponding to a string representation. Input string has constraints
80      * on what characters it can contain. It may contain the following:
81      * - US-ASCII letters
82      * - special characters: -_.*+:=,!~';
83      *
84      * @return A {@link FrontendType} instance
85      * @throws IllegalArgumentException if the string is null, empty or contains invalid characters
86      */
87     public static FrontendType forName(final String name) {
88         Preconditions.checkArgument(!Strings.isNullOrEmpty(name));
89         Preconditions.checkArgument(SIMPLE_STRING_PATTERN.matcher(name).matches());
90         return new FrontendType(name);
91     }
92
93     public static FrontendType readFrom(final DataInput in) throws IOException {
94         final byte[] serialized = new byte[in.readInt()];
95         in.readFully(serialized);
96         return new FrontendType(new String(serialized, StandardCharsets.UTF_8));
97     }
98
99     @Override
100     public void writeTo(final DataOutput out) throws IOException {
101         final byte[] serialized = getSerialized();
102         out.writeInt(serialized.length);
103         out.write(serialized);
104     }
105
106     public String getName() {
107         return name;
108     }
109
110     @Override
111     public int hashCode() {
112         return name.hashCode();
113     }
114
115     @Override
116     public boolean equals(final Object o) {
117         return this == o || (o instanceof FrontendType && name.equals(((FrontendType)o).name));
118     }
119
120     @Override
121     public int compareTo(final FrontendType o) {
122         return this == o ? 0 : name.compareTo(o.name);
123     }
124
125     @Override
126     public String toString() {
127         return MoreObjects.toStringHelper(FrontendType.class).add("name", name).toString();
128     }
129
130     private byte[] getSerialized() {
131         byte[] local = serialized;
132         if (local == null) {
133             local = name.getBytes(StandardCharsets.UTF_8);
134             serialized = local;
135         }
136         return local;
137     }
138
139     Object writeReplace() {
140         return new Proxy(getSerialized());
141     }
142 }