1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.webmacro.util;
24
25 import org.webmacro.Broker;
26 import org.webmacro.InitException;
27 import org.webmacro.Log;
28 import org.webmacro.ResourceException;
29 import org.webmacro.resource.CacheElement;
30 import org.webmacro.resource.CacheManager;
31 import org.webmacro.resource.ResourceLoader;
32 import org.webmacro.resource.TrivialCacheManager;
33
34 import java.io.UnsupportedEncodingException;
35
36 /***
37 * An encoder is used to encode strings into a particular encoding in
38 * preparation for sending the data as part of a response. An encoder is
39 * constructed with a particular output encoding and is then expected to
40 * properly encode strings with that output encoding for its lifetime.
41 *
42 * <p><code>Encoder</code> instances are obtained via the
43 * <code>EncoderProvider</code> implementation that is configured at
44 * initialization time. The encoder provider is responsible communicating
45 * the encoding scheme to be used by the encoder at construct time.
46 *
47 * <p> The mechanism is factored into the encoder interface to allow for
48 * caching encoders with differing caching schemes based on the server
49 * environment's requirements or no caching scheme at all.
50 *
51 * @see EncoderProvider
52 * @since 0.96
53 * @author Michael Bayne
54 */
55
56 public class Encoder implements ResourceLoader
57 {
58
59 private String _encoding;
60 private CacheManager _cache;
61 private Log _log;
62
63 /***
64 * Creates an encoder instance with the supplied encoding.
65 *
66 * @exception UnsupportedEncodingException Thrown when the underlying
67 * Java encoding mechanism does not provide support for the requesting
68 * encoding.
69 */
70 public Encoder (String encoding)
71 throws UnsupportedEncodingException
72 {
73
74 if (encoding == null ||
75 encoding.equalsIgnoreCase("UNICODE") ||
76 encoding.equalsIgnoreCase("UNICODEBIG") ||
77 encoding.equalsIgnoreCase("UNICODELITTLE") ||
78 encoding.equalsIgnoreCase("UTF16"))
79 {
80 String err = "The encoding you specified is invalid: " +
81 encoding + ". Note that the UNICODE and UTF16 encodings " +
82 "are not supported by WebMacro because they prefix the " +
83 "stream with a marker indicating whether the stream is " +
84 "big endian or little endian. Instead choose the byte " +
85 "ordering yourself by using the UTF-16BE or UTF-16LE " +
86 "encodings.";
87 throw new UnsupportedEncodingException(err);
88 }
89
90
91
92
93 byte[] test = "some test string".getBytes(encoding);
94
95
96 _encoding = encoding;
97 }
98
99 public void init (Broker b, Settings config)
100 throws InitException
101 {
102 String cacheManager;
103 _log = b.getLog("resource", "Object loading and caching");
104
105 cacheManager = b.getSetting("Encoder." + _encoding + ".CacheManager");
106 if (cacheManager == null)
107 cacheManager = b.getSetting("Encoder.*.CacheManager");
108 if (cacheManager == null || cacheManager.equals(""))
109 {
110 _log.info("No cache manager specified for encoding " + _encoding
111 + ", using TrivialCacheManager");
112 _cache = new TrivialCacheManager();
113 }
114 else
115 {
116 try
117 {
118 Class c = b.classForName(cacheManager);
119 _cache = (CacheManager) c.newInstance();
120 }
121 catch (Exception e)
122 {
123 _log.warning("Unable to load cache manager " + cacheManager
124 + " for encoding type " + _encoding
125 + ", using TrivialCacheManager. Reason:\n" + e);
126 _cache = new TrivialCacheManager();
127 }
128 }
129 _cache.init(b, config, _encoding);
130 }
131
132 /***
133 * Load an object from permanent storage (or construct it) on
134 * demand.
135 */
136 public Object load (Object query, CacheElement ce)
137 throws ResourceException
138 {
139 try
140 {
141 if (query instanceof Block)
142 {
143 String[] source = ((Block) query).text;
144 byte[][] encoded = new byte[source.length][];
145 for (int i = 0; i < source.length; i++)
146 {
147 encoded[i] = source[i].getBytes(_encoding);
148 }
149 return encoded;
150
151 }
152 else if (query instanceof String)
153 {
154 return ((String) query).getBytes(_encoding);
155 }
156 else
157 {
158 return query.toString().getBytes(_encoding);
159 }
160
161 }
162 catch (UnsupportedEncodingException uee)
163 {
164
165
166 throw new ResourceException("Unable to encode: " + uee);
167 }
168 }
169
170 public Object load (String query, CacheElement ce)
171 throws ResourceException
172 {
173 try
174 {
175 return query.getBytes(_encoding);
176 }
177 catch (UnsupportedEncodingException uee)
178 {
179
180
181 throw new ResourceException("Unable to encode: " + uee);
182 }
183 }
184
185 /***
186 * Encodes the supplied string using the encoding bound to this encoder
187 * at construct time.
188 *
189 * @return The encoded version of the supplied string.
190 *
191 * @exception UnsupportedEncodingException Thrown when the underlying
192 * Java encoding mechanism does not provide support for the encoding
193 * used by this encoder instance.
194 */
195 public final byte[] encode (String source)
196 throws UnsupportedEncodingException
197 {
198 try
199 {
200 return (byte[]) _cache.get(source, this);
201 }
202 catch (ResourceException e)
203 {
204 throw new UnsupportedEncodingException("Encoder: Could not encode; "
205 + e);
206 }
207 }
208
209 /***
210 * Encodes the supplied block of strings using the encoding bound to
211 * this encoder at construct time.
212 *
213 * @return The encoded version of the supplied block of strings.
214 *
215 * @exception UnsupportedEncodingException Thrown when the underlying
216 * Java encoding mechanism does not provide support for the encoding
217 * used by this encoder instance.
218 */
219 public final byte[][] encode (Block source)
220 throws UnsupportedEncodingException
221 {
222 try
223 {
224 return (byte[][]) _cache.get(source, this);
225 }
226 catch (ResourceException e)
227 {
228 throw new UnsupportedEncodingException("Encoder: Could not encode; "
229 + e);
230 }
231 }
232
233 /***
234 * The block class provides a means by which encoder users can encode
235 * entire blocks of text at once (and have those encoded blocks
236 * cached).
237 */
238 public static class Block
239 {
240
241 public String[] text;
242
243 public Block (String[] text)
244 {
245 this.text = text;
246
247
248
249
250 long strhash = 0;
251 for (int i = 0; i < text.length; i++)
252 {
253 strhash = (strhash + (long) text[i].hashCode()) %
254 Integer.MAX_VALUE;
255 }
256 _hashCode = (int) strhash;
257 }
258
259 public int hashCode ()
260 {
261 return _hashCode;
262 }
263
264 public boolean equals (Object other)
265 {
266
267
268 if (!(other instanceof Block))
269 {
270 return false;
271 }
272
273 Block ob = (Block) other;
274
275
276 if (this == ob || text == ob.text)
277 {
278 return true;
279 }
280 if (text == null || ob.text == null)
281 {
282 return false;
283 }
284 if (ob.text.length != text.length)
285 {
286 return false;
287 }
288
289
290 for (int i = 0; i < text.length; i++)
291 {
292 if (!text[i].equals(ob.text[i]))
293 {
294 return false;
295 }
296 }
297
298 return true;
299 }
300
301 protected int _hashCode;
302 }
303 }