001: /*
002: * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
003: *
004: * The Apache Software License, Version 1.1
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution, if
019: * any, must include the following acknowlegement:
020: * "This product includes software developed by the
021: * Caucho Technology (http://www.caucho.com/)."
022: * Alternately, this acknowlegement may appear in the software itself,
023: * if and wherever such third-party acknowlegements normally appear.
024: *
025: * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
026: * endorse or promote products derived from this software without prior
027: * written permission. For written permission, please contact
028: * info@caucho.com.
029: *
030: * 5. Products derived from this software may not be called "Resin"
031: * nor may "Resin" appear in their names without prior written
032: * permission of Caucho Technology.
033: *
034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
038: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
039: * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
040: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
041: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
042: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
043: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
044: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045: *
046: * @author Scott Ferguson
047: */
048:
049: package com.caucho.hessian.io;
050:
051: import java.io.IOException;
052: import java.lang.reflect.Method;
053: import java.lang.reflect.Modifier;
054: import java.util.ArrayList;
055: import java.util.logging.*;
056:
057: /**
058: * Serializing an object for known object types.
059: */
060: public class BeanSerializer extends AbstractSerializer {
061: private static final Object[] NULL_ARGS = new Object[0];
062: private Method[] _methods;
063: private String[] _names;
064:
065: private Method _writeReplace;
066:
067: public BeanSerializer(Class cl) {
068: _writeReplace = getWriteReplace(cl);
069:
070: ArrayList primitiveMethods = new ArrayList();
071: ArrayList compoundMethods = new ArrayList();
072:
073: for (; cl != null; cl = cl.getSuperclass()) {
074: Method[] methods = cl.getDeclaredMethods();
075:
076: for (int i = 0; i < methods.length; i++) {
077: Method method = methods[i];
078:
079: if (Modifier.isStatic(method.getModifiers()))
080: continue;
081:
082: if (method.getParameterTypes().length != 0)
083: continue;
084:
085: String name = method.getName();
086:
087: if (!name.startsWith("get"))
088: continue;
089:
090: Class type = method.getReturnType();
091:
092: if (type.equals(void.class))
093: continue;
094:
095: if (findSetter(methods, name, type) == null)
096: continue;
097:
098: // XXX: could parameterize the handler to only deal with public
099: method.setAccessible(true);
100:
101: if (type.isPrimitive()
102: || type.getName().startsWith("java.lang.")
103: && !type.equals(Object.class))
104: primitiveMethods.add(method);
105: else
106: compoundMethods.add(method);
107: }
108: }
109:
110: ArrayList methodList = new ArrayList();
111: methodList.addAll(primitiveMethods);
112: methodList.addAll(compoundMethods);
113:
114: _methods = new Method[methodList.size()];
115: methodList.toArray(_methods);
116:
117: _names = new String[_methods.length];
118:
119: for (int i = 0; i < _methods.length; i++) {
120: String name = _methods[i].getName();
121:
122: name = name.substring(3);
123:
124: int j = 0;
125: for (; j < name.length()
126: && Character.isUpperCase(name.charAt(j)); j++) {
127: }
128:
129: if (j == 1)
130: name = name.substring(0, j).toLowerCase()
131: + name.substring(j);
132: else if (j > 1)
133: name = name.substring(0, j - 1).toLowerCase()
134: + name.substring(j - 1);
135:
136: _names[i] = name;
137: }
138: }
139:
140: /**
141: * Returns the writeReplace method
142: */
143: protected Method getWriteReplace(Class cl) {
144: for (; cl != null; cl = cl.getSuperclass()) {
145: Method[] methods = cl.getDeclaredMethods();
146:
147: for (int i = 0; i < methods.length; i++) {
148: Method method = methods[i];
149:
150: if (method.getName().equals("writeReplace")
151: && method.getParameterTypes().length == 0)
152: return method;
153: }
154: }
155:
156: return null;
157: }
158:
159: public void writeObject(Object obj, AbstractHessianOutput out)
160: throws IOException {
161: if (out.addRef(obj))
162: return;
163:
164: Class cl = obj.getClass();
165:
166: try {
167: if (_writeReplace != null) {
168: Object repl = _writeReplace.invoke(obj, new Object[0]);
169:
170: out.removeRef(obj);
171:
172: out.writeObject(repl);
173:
174: out.replaceRef(repl, obj);
175:
176: return;
177: }
178: } catch (Exception e) {
179: }
180:
181: int ref = out.writeObjectBegin(cl.getName());
182:
183: if (ref < -1) {
184: // Hessian 1.1 uses a map
185:
186: for (int i = 0; i < _methods.length; i++) {
187: Method method = _methods[i];
188: Object value = null;
189:
190: try {
191: value = _methods[i].invoke(obj, (Object[]) null);
192: } catch (Exception e) {
193: log.log(Level.FINE, e.toString(), e);
194: }
195:
196: out.writeString(_names[i]);
197:
198: out.writeObject(value);
199: }
200:
201: out.writeMapEnd();
202: } else {
203: if (ref == -1) {
204: out.writeInt(_names.length);
205:
206: for (int i = 0; i < _names.length; i++)
207: out.writeString(_names[i]);
208:
209: out.writeObjectBegin(cl.getName());
210: }
211:
212: for (int i = 0; i < _methods.length; i++) {
213: Method method = _methods[i];
214: Object value = null;
215:
216: try {
217: value = _methods[i].invoke(obj, (Object[]) null);
218: } catch (Exception e) {
219: log.log(Level.FINER, e.toString(), e);
220: }
221:
222: out.writeObject(value);
223: }
224: }
225: }
226:
227: /**
228: * Finds any matching setter.
229: */
230: private Method findSetter(Method[] methods, String getterName,
231: Class arg) {
232: String setterName = "set" + getterName.substring(3);
233:
234: for (int i = 0; i < methods.length; i++) {
235: Method method = methods[i];
236:
237: if (!method.getName().equals(setterName))
238: continue;
239:
240: if (!method.getReturnType().equals(void.class))
241: continue;
242:
243: Class[] params = method.getParameterTypes();
244:
245: if (params.length == 1 && params[0].equals(arg))
246: return method;
247: }
248:
249: return null;
250: }
251: }
|