001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2007-2010 ADEQUATE SYSTEMS SARL
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.toplink;
022
023 import java.io.IOException;
024 import java.io.ObjectInput;
025 import java.io.ObjectOutput;
026 import java.lang.reflect.InvocationTargetException;
027 import java.lang.reflect.Type;
028 import java.util.ArrayList;
029 import java.util.HashMap;
030 import java.util.List;
031 import java.util.Map;
032 import java.util.Set;
033
034 import javax.persistence.Embeddable;
035 import javax.persistence.Entity;
036 import javax.persistence.IdClass;
037 import javax.persistence.MappedSuperclass;
038
039 import oracle.toplink.essentials.indirection.IndirectContainer;
040 import oracle.toplink.essentials.indirection.IndirectList;
041 import oracle.toplink.essentials.indirection.IndirectMap;
042 import oracle.toplink.essentials.indirection.IndirectSet;
043 import oracle.toplink.essentials.indirection.ValueHolderInterface;
044
045 import org.granite.config.GraniteConfig;
046 import org.granite.context.GraniteContext;
047 import org.granite.logging.Logger;
048 import org.granite.messaging.amf.io.convert.Converters;
049 import org.granite.messaging.amf.io.util.ClassGetter;
050 import org.granite.messaging.amf.io.util.MethodProperty;
051 import org.granite.messaging.amf.io.util.Property;
052 import org.granite.messaging.amf.io.util.externalizer.DefaultExternalizer;
053 import org.granite.messaging.amf.io.util.externalizer.annotation.ExternalizedProperty;
054 import org.granite.messaging.persistence.AbstractExternalizablePersistentCollection;
055 import org.granite.messaging.persistence.ExternalizablePersistentList;
056 import org.granite.messaging.persistence.ExternalizablePersistentMap;
057 import org.granite.messaging.persistence.ExternalizablePersistentSet;
058 import org.granite.util.ClassUtil;
059
060 /**
061 * @author William DRAI
062 */
063 public class TopLinkExternalizer extends DefaultExternalizer {
064
065 private static final Logger log = Logger.getLogger(TopLinkExternalizer.class);
066
067 @Override
068 public Object newInstance(String type, ObjectInput in)
069 throws IOException, ClassNotFoundException, InstantiationException, InvocationTargetException, IllegalAccessException {
070
071 // If type is not an entity (@Embeddable for example), we don't read initialized/detachedState
072 // and we fall back to DefaultExternalizer behavior.
073 Class<?> clazz = ClassUtil.forName(type);
074 if (!isRegularEntity(clazz))
075 return super.newInstance(type, in);
076
077 // Read initialized flag.
078 boolean initialized = ((Boolean)in.readObject()).booleanValue();
079
080 // Read detached state.
081 @SuppressWarnings("unused")
082 String detachedState = (String)in.readObject();
083
084 // New or initialized entity.
085 if (initialized)
086 return super.newInstance(type, in);
087
088 // Pseudo-proxy (uninitialized entity).
089 Object id = in.readObject();
090 if (id != null && (!clazz.isAnnotationPresent(IdClass.class) || !clazz.getAnnotation(IdClass.class).value().equals(id.getClass())))
091 throw new RuntimeException("Id for TopLink pseudo-proxy should be null or IdClass (" + type + ")");
092
093 return new TopLinkValueHolder();
094 }
095
096 @Override
097 public void readExternal(Object o, ObjectInput in) throws IOException, ClassNotFoundException, IllegalAccessException {
098 // Ignore TopLink proxies
099 if (o instanceof TopLinkValueHolder)
100 return;
101
102 // @Embeddable or others...
103 if (!isRegularEntity(o.getClass())) {
104 log.debug("Delegating non regular entity reading to DefaultExternalizer...");
105 super.readExternal(o, in);
106 }
107 // Regural @Entity or @MappedSuperclass
108 else {
109 GraniteConfig config = GraniteContext.getCurrentInstance().getGraniteConfig();
110
111 Converters converters = config.getConverters();
112 ClassGetter classGetter = config.getClassGetter();
113 Class<?> oClass = classGetter.getClass(o);
114
115 List<Property> fields = findOrderedFields(oClass);
116 log.debug("Reading entity %s with fields %s", oClass.getName(), fields);
117 Map<String, Property> topLinkFields = new HashMap<String, Property>();
118 for (Property field : fields) {
119 if (field.getType() instanceof Class<?> && ValueHolderInterface.class.isAssignableFrom((Class<?>)field.getType())) {
120 topLinkFields.put(field.getName(), field);
121 }
122 else {
123 Object value = in.readObject();
124
125 if (value instanceof ValueHolderInterface) {
126 topLinkFields.get("_toplink_" + field.getName() + "_vh").setProperty(o, value, false);
127 }
128 else if (!(field instanceof MethodProperty && field.isAnnotationPresent(ExternalizedProperty.class))) {
129 if (value instanceof AbstractExternalizablePersistentCollection)
130 value = newIndirectCollection((AbstractExternalizablePersistentCollection)value, field.getType());
131 else
132 value = converters.convert(value, field.getType());
133
134 field.setProperty(o, value, false);
135 }
136 }
137 }
138 }
139 }
140
141 protected IndirectContainer newIndirectCollection(AbstractExternalizablePersistentCollection value, Type target) {
142 final boolean initialized = value.isInitialized();
143 final Object[] content = value.getContent();
144
145 IndirectContainer coll = null;
146 if (value instanceof ExternalizablePersistentSet) {
147 if (initialized) {
148 if (content != null) {
149 Set<?> set = ((ExternalizablePersistentSet)value).getContentAsSet(target);
150 coll = new IndirectSet(set);
151 }
152 }
153 else
154 coll = new IndirectSet();
155 }
156 else if (value instanceof ExternalizablePersistentList) {
157 if (initialized) {
158 if (content != null) {
159 List<?> list = ((ExternalizablePersistentList)value).getContentAsList(target);
160 coll = new IndirectList(list);
161 }
162 }
163 else
164 coll = new IndirectList();
165 }
166 else if (value instanceof ExternalizablePersistentMap) {
167 if (initialized) {
168 if (content != null) {
169 Map<?, ?> map = ((ExternalizablePersistentMap)value).getContentAsMap(target);
170 coll = new IndirectMap(map);
171 }
172 }
173 else
174 coll = new IndirectMap();
175 }
176 else {
177 throw new RuntimeException("Illegal externalizable persitent class: " + value);
178 }
179
180 return coll;
181 }
182
183
184 @Override
185 public void writeExternal(Object o, ObjectOutput out) throws IOException, IllegalAccessException {
186
187 ClassGetter classGetter = GraniteContext.getCurrentInstance().getGraniteConfig().getClassGetter();
188 Class<?> oClass = classGetter.getClass(o);
189
190 if (o instanceof TopLinkProxy) {
191 TopLinkProxy proxy = (TopLinkProxy)o;
192
193 // Only write initialized flag, null detachedState & null id if proxy is uninitialized.
194 log.debug("Writing uninitialized TopLink ValueHolder %s", proxy.getProxiedClass().getName());
195
196 // Write initialized flag.
197 out.writeObject(Boolean.FALSE);
198 // Write detachedState.
199 out.writeObject(null);
200 // Write id.
201 out.writeObject(null);
202
203 return;
204 }
205
206 if (!isRegularEntity(o.getClass())) { // @Embeddable or others...
207 log.debug("Delegating non regular entity writing to DefaultExternalizer...");
208 super.writeExternal(o, out);
209 }
210 else {
211 // Write initialized flag.
212 out.writeObject(Boolean.TRUE);
213 // Write detachedState.
214 out.writeObject(null);
215
216 // Externalize entity fields.
217 List<Property> fields = findOrderedFields(oClass);
218 List<String> lazyFieldNames = new ArrayList<String>();
219
220 log.debug("Writing entity %s with fields %s", o.getClass().getName(), fields);
221 for (Property field : fields) {
222 if (!(field.getType() instanceof Class<?> && ValueHolderInterface.class.isAssignableFrom((Class<?>)field.getType()))) {
223 if (lazyFieldNames.contains(field.getName())) {
224 TopLinkProxy proxy = new TopLinkProxy((Class<?>)field.getType());
225 out.writeObject(proxy);
226 }
227 else {
228 Object value = field.getProperty(o);
229
230 if (value instanceof IndirectContainer)
231 value = newExternalizableCollection((IndirectContainer)value);
232
233 out.writeObject(value);
234 }
235 }
236 else {
237 ValueHolderInterface vh = (ValueHolderInterface)field.getProperty(o);
238 if (!vh.isInstantiated())
239 lazyFieldNames.add(field.getName().substring("_toplink_".length(), field.getName().length()-3));
240 }
241 }
242 }
243 }
244
245 protected AbstractExternalizablePersistentCollection newExternalizableCollection(IndirectContainer value) {
246 AbstractExternalizablePersistentCollection coll = null;
247 boolean initialized = value.isInstantiated();
248
249 if (value instanceof IndirectSet) {
250 coll = new ExternalizablePersistentSet(initialized ? ((IndirectSet)value).toArray() : null, initialized, false);
251 }
252 else if (value instanceof IndirectList) {
253 coll = new ExternalizablePersistentList(initialized ? ((IndirectList)value).toArray() : null, initialized, false);
254 }
255 else if (value instanceof IndirectMap) {
256 Object[] content = null;
257
258 if (initialized) {
259 content = new Object[((IndirectMap)value).size()];
260 int index = 0;
261 @SuppressWarnings("unchecked")
262 Set<Map.Entry<?, ?>> entries = ((IndirectMap)value).entrySet();
263 for (Map.Entry<?, ?> entry : entries)
264 content[index++] = new Object[]{entry.getKey(), entry.getValue()};
265 }
266
267 coll = new ExternalizablePersistentMap(content, initialized, false);
268 }
269 else {
270 throw new UnsupportedOperationException("Unsupported TopLink collection type: " + value);
271 }
272
273 return coll;
274 }
275
276
277 @Override
278 public int accept(Class<?> clazz) {
279 return (
280 clazz.isAnnotationPresent(Entity.class) ||
281 clazz.isAnnotationPresent(MappedSuperclass.class) ||
282 clazz.isAnnotationPresent(Embeddable.class)
283 ) ? 1 : -1;
284 }
285
286 protected boolean isRegularEntity(Class<?> clazz) {
287 return clazz.isAnnotationPresent(Entity.class) || clazz.isAnnotationPresent(MappedSuperclass.class);
288 }
289 }