/* * Created on 26.08.2004 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package com.webobjects.directtoweb; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOSession; import com.webobjects.eoaccess.EOAttribute; import com.webobjects.eoaccess.EOEntity; import com.webobjects.eoaccess.EOModelGroup; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOEnterpriseObject; /** * Optimizes custom attribute handling and fixes a problem when * a context can't find its task or entity even though it is given in the rules. * @author david caching * @author ak factory, thread safety, fix */ public class ERD2WContext extends D2WContext implements Serializable { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; private static Map customAttributes = new HashMap(); private static final Object NOT_FOUND = new Object(); static { if(WOApplication.application().isConcurrentRequestHandlingEnabled()) { customAttributes = Collections.synchronizedMap(customAttributes); } } /** * Factory to create D2WContext's. You can provide your own subclass and * set it via {@link #setFactory(Factory)}. The static methods newContext(...) * should be used throughout ERD2W. * @author ak */ public static class Factory { public D2WContext newContext() { return new ERD2WContext(); } public D2WContext newContext(WOSession session) { return new ERD2WContext(session); } public D2WContext newContext(D2WContext context) { return new ERD2WContext(context); } } private static Factory _factory = new Factory(); public static D2WContext newContext() { return _factory.newContext(); } public static D2WContext newContext(WOSession session) { return _factory.newContext(session); } public static D2WContext newContext(D2WContext context) { return _factory.newContext(context); } public static void setFactory(Factory factory) { _factory = factory; } public ERD2WContext() { super(); } public ERD2WContext(WOSession session) { super(session); } public ERD2WContext(D2WContext session) { super(session); } /** * Overrridden because when a page config is set, task and entity are cleared, * but not re-set when you just call task() or entity(). This leads to NPEs, * errors that a pageName can't be found and others. Setting it here fixes it. */ @Override public void setDynamicPage(String page) { super.setDynamicPage(page); setTask(task()); setEntity(entity()); } /** * Overridden so that custom attributes are cached as a performance * optimization. */ @Override EOAttribute customAttribute(String s, EOEntity eoentity) { String s1 = eoentity.name() + "." + s; Object o = customAttributes.get(s1); if(o == NOT_FOUND) { return null; } EOAttribute eoattribute = (EOAttribute)o; if (eoattribute == null && s != null) { Class class1 = D2WUtils.dataTypeForCustomKeyAndEntity(s, eoentity); if (class1 != null) { eoattribute = new EOAttribute(); eoattribute.setName(s); eoattribute.setClassName(class1.getName()); customAttributes.put(s1, eoattribute); } else { // this should be cached, too // can save up to 100 millis and more for complex pages customAttributes.put(s1, NOT_FOUND); } } return eoattribute; } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { takeValueForKey(in.readObject(), D2WModel.SessionKey); takeValueForKey(in.readBoolean()?D2WModel.One:D2WModel.Zero, D2WModel.FrameKey); takeValueForKey(in.readObject(), D2WModel.TaskKey); String entityName = (String) in.readObject(); EOEntity entity = entityName == null?null:EOModelGroup.defaultGroup().entityNamed(entityName); takeValueForKey(entity, D2WModel.EntityKey); takeValueForKey(in.readObject(), D2WModel.PropertyKeyKey); takeValueForKey(in.readObject(), D2WModel.DynamicPageKey); /* * The ec must be deserialized before the EO. Otherwise, when the EO is * deserialized, it attempts to deserialize the EC, which turns around * and tries to deserialize the EO again. The EO is returned in its partially * deserialized state, which results in a NullPointerException when the EC * starts to try to load values into the EO's dictionary... which is null. */ EOEditingContext ec = (EOEditingContext) in.readObject(); takeValueForKey(in.readObject(), "object"); } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(valueForKey(D2WModel.SessionKey)); out.writeBoolean(frame()); out.writeObject(task()); out.writeObject(entity() == null?null:entity().name()); out.writeObject(propertyKey()); out.writeObject(dynamicPage()); EOEnterpriseObject obj = (EOEnterpriseObject) valueForKey("object"); EOEditingContext ec = (obj == null || obj.editingContext() == null)?null:obj.editingContext(); /* * The ec must be deserialized before the EO. Otherwise, when the EO is * deserialized, it attempts to deserialize the EC, which turns around * and tries to deserialize the EO again. The EO is returned in its partially * deserialized state, which results in a NullPointerException when the EC * starts to try to load values into the EO's dictionary... which is null. */ out.writeObject(ec); /* * If a create page is cancelled, the object is deleted. When that happens, * the ec is null. Writing the EO without an EC results with the same error * as not writing the ec at all. */ out.writeObject(ec==null?null:obj); } }