Search code examples
eclipseserializationemf

NotSerializableException on EMF entity serialization


I have java application uses EMF. I run it on WildFly 10.
I use Eclipse Neon, and JDK8.0.
I deploy the EAR to WildFly and debug it.
All I need is serialize instancfe of my EMF Audit class to XML string.
Here is Audit class:

public class AuditImpl extends MinimalEObjectImpl.Container implements Audit {
    protected static final String OBJECT_ID_EDEFAULT = null;
    protected String object_id = OBJECT_ID_EDEFAULT;
    protected static final ObjectType OBJECT_TYPE_EDEFAULT = ObjectType.CLIENT;
    protected ObjectType object_type = OBJECT_TYPE_EDEFAULT;
    protected static final AuditAction ACTION_EDEFAULT = AuditAction.CREATE;
    protected AuditAction action = ACTION_EDEFAULT;
    protected static final Date ACTION_DATE_EDEFAULT = null;
    protected Date action_date = ACTION_DATE_EDEFAULT;
    protected static final Object NEW_VALUE_EDEFAULT = null;
    protected Object new_value = NEW_VALUE_EDEFAULT;
    protected AuditImpl() {
        super();
    }

    @Override
    protected EClass eStaticClass() {
        return NeoflexAuditPackage.Literals.AUDIT;
    }

    public String getObject_id() {
        return object_id;
    }

    public void setObject_id(String newObject_id) {
        String oldObject_id = object_id;
        object_id = newObject_id;
        if (eNotificationRequired())
            eNotify(new ENotificationImpl(this, Notification.SET, NeoflexAuditPackage.AUDIT__OBJECT_ID, oldObject_id, object_id));
    }

    public ObjectType getObject_type() {
        return object_type;
    }

    public void setObject_type(ObjectType newObject_type) {
        ObjectType oldObject_type = object_type;
        object_type = newObject_type == null ? OBJECT_TYPE_EDEFAULT : newObject_type;
        if (eNotificationRequired())
            eNotify(new ENotificationImpl(this, Notification.SET, NeoflexAuditPackage.AUDIT__OBJECT_TYPE, oldObject_type, object_type));
    }

    public AuditAction getAction() {
        return action;
    }

    public void setAction(AuditAction newAction) {
        AuditAction oldAction = action;
        action = newAction == null ? ACTION_EDEFAULT : newAction;
        if (eNotificationRequired())
            eNotify(new ENotificationImpl(this, Notification.SET, NeoflexAuditPackage.AUDIT__ACTION, oldAction, action));
    }

    public Date getAction_date() {
        return action_date;
    }

    public void setAction_date(Date newAction_date) {
        Date oldAction_date = action_date;
        action_date = newAction_date;
        if (eNotificationRequired())
            eNotify(new ENotificationImpl(this, Notification.SET, NeoflexAuditPackage.AUDIT__ACTION_DATE, oldAction_date, action_date));
    }

    public Object getNew_value() {
        return new_value;
    }

    public void setNew_value(Object newNew_value) {
        Object oldNew_value = new_value;
        new_value = newNew_value;
        if (eNotificationRequired())
            eNotify(new ENotificationImpl(this, Notification.SET, NeoflexAuditPackage.AUDIT__NEW_VALUE, oldNew_value, new_value));
    }

    @Override
    public Object eGet(int featureID, boolean resolve, boolean coreType) {
        switch (featureID) {
            case NeoflexAuditPackage.AUDIT__OBJECT_ID:
                return getObject_id();
            case NeoflexAuditPackage.AUDIT__OBJECT_TYPE:
                return getObject_type();
            case NeoflexAuditPackage.AUDIT__ACTION:
                return getAction();
            case NeoflexAuditPackage.AUDIT__ACTION_DATE:
                return getAction_date();
            case NeoflexAuditPackage.AUDIT__NEW_VALUE:
                return getNew_value();
        }
        return super.eGet(featureID, resolve, coreType);
    }

    @Override
    public void eSet(int featureID, Object newValue) {
        switch (featureID) {
            case NeoflexAuditPackage.AUDIT__OBJECT_ID:
                setObject_id((String)newValue);
                return;
            case NeoflexAuditPackage.AUDIT__OBJECT_TYPE:
                setObject_type((ObjectType)newValue);
                return;
            case NeoflexAuditPackage.AUDIT__ACTION:
                setAction((AuditAction)newValue);
                return;
            case NeoflexAuditPackage.AUDIT__ACTION_DATE:
                setAction_date((Date)newValue);
                return;
            case NeoflexAuditPackage.AUDIT__NEW_VALUE:
                setNew_value(newValue);
                return;
        }
        super.eSet(featureID, newValue);
    }

    @Override
    public void eUnset(int featureID) {
        switch (featureID) {
            case NeoflexAuditPackage.AUDIT__OBJECT_ID:
                setObject_id(OBJECT_ID_EDEFAULT);
                return;
            case NeoflexAuditPackage.AUDIT__OBJECT_TYPE:
                setObject_type(OBJECT_TYPE_EDEFAULT);
                return;
            case NeoflexAuditPackage.AUDIT__ACTION:
                setAction(ACTION_EDEFAULT);
                return;
            case NeoflexAuditPackage.AUDIT__ACTION_DATE:
                setAction_date(ACTION_DATE_EDEFAULT);
                return;
            case NeoflexAuditPackage.AUDIT__NEW_VALUE:
                setNew_value(NEW_VALUE_EDEFAULT);
                return;
        }
        super.eUnset(featureID);
    }

    @Override
    public boolean eIsSet(int featureID) {
        switch (featureID) {
            case NeoflexAuditPackage.AUDIT__OBJECT_ID:
                return OBJECT_ID_EDEFAULT == null ? object_id != null : !OBJECT_ID_EDEFAULT.equals(object_id);
            case NeoflexAuditPackage.AUDIT__OBJECT_TYPE:
                return object_type != OBJECT_TYPE_EDEFAULT;
            case NeoflexAuditPackage.AUDIT__ACTION:
                return action != ACTION_EDEFAULT;
            case NeoflexAuditPackage.AUDIT__ACTION_DATE:
                return ACTION_DATE_EDEFAULT == null ? action_date != null : !ACTION_DATE_EDEFAULT.equals(action_date);
            case NeoflexAuditPackage.AUDIT__NEW_VALUE:
                return NEW_VALUE_EDEFAULT == null ? new_value != null : !NEW_VALUE_EDEFAULT.equals(new_value);
        }
        return super.eIsSet(featureID);
    }

    @Override
    public String toString() {
        if (eIsProxy()) return super.toString();

        StringBuffer result = new StringBuffer(super.toString());
        result.append(" (object_id: ");
        result.append(object_id);
        result.append(", object_type: ");
        result.append(object_type);
        result.append(", action: ");
        result.append(action);
        result.append(", action_date: ");
        result.append(action_date);
        result.append(", new_value: ");
        result.append(new_value);
        result.append(')');
        return result.toString();
    }

} //AuditImpl

Here is my function:

private String getXml(Audit audit) throws NeoflexException {
    XMLResource res = new XMLResourceImpl();        
    res.getContents().add(audit);       
    StringWriter sw = new StringWriter();

    try {
        res.save(sw, null);
    } catch (IOException e) {
        throw new NeoflexException(e);
    }

    return sw.toString();   
}

On res.save(sw, null); I get:

java.lang.RuntimeException: java.io.NotSerializableException: NeoflexAudit.impl.AccountImpl
org.eclipse.emf.ecore.impl.EFactoryImpl.convertToString(EFactoryImpl.java:692)
org.eclipse.emf.ecore.impl.EcoreFactoryImpl.convertEJavaObjectToString(EcoreFactoryImpl.java:937)
org.eclipse.emf.ecore.impl.EcoreFactoryImpl.convertToString(EcoreFactoryImpl.java:209)
org.eclipse.emf.ecore.xmi.impl.XMLHelperImpl.convertToString(XMLHelperImpl.java:1610)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.getDatatypeValue(XMLSaveImpl.java:3108)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.saveDataTypeSingle(XMLSaveImpl.java:1698)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.saveFeatures(XMLSaveImpl.java:1280)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.saveFeatures(XMLSaveImpl.java:1224)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.saveElementID(XMLSaveImpl.java:2716)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.writeTopObject(XMLSaveImpl.java:683)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.traverse(XMLSaveImpl.java:591)
org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl.save(XMLSaveImpl.java:225)

EDIT

OK, I see that class must be serialazible, but I can not achive it via EMF modeling view... And it is wrong to edit generated code.
So I changed the function to:

private void persistAudit(Audit audit) throws NeoflexException {
    ResourceSet resourceSet = new ResourceSetImpl();
    resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xml", new XMLResourceFactoryImpl());

    URI fileURI = URI.createFileURI(new File("mypo.xml").getAbsolutePath());
    Resource poResource = resourceSet.createResource(fileURI);
    poResource.getContents().add(audit);

    try {
        poResource.save(null);
    } catch (IOException e) {
        throw new NeoflexException(e);
    }
}

I used:

resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("xml", new XMLResourceFactoryImpl());

as described here: "Registered factory needed" exception when loading resource
But I still have null in poResource.


Solution

  • The resource factory approach is the way to go. The only thing I see missing is that the ResourceSet also needs to know about your metamodel: the class and attribute information is required to persist the model.

    The provided approach uses a StringWritter so you can actually get a string with the XML (probably needed to send your response ;) )

    private String getAuditXML(Audit audit) throws NeoflexException {
        ResourceSet resourceSet = new ResourceSetImpl();
        // In standalone we need to register the Ecore metamodel too
        EcorePackage.eINSTANCE.eClass();
        // From your post I assume yuo have the generated EPackage of your metamodel,
        // if not you need to load the ecore file as a resource too, get the EPackage and
        // register it
        AuditPackage.eINSTANCE.eClass();
        // Ecore models should be persisted as XMI (which is just an XML document
        // with an additional namesapce, so it should work for you).
        resourceSet.getResourceFactoryRegistry()
            .getExtensionToFactoryMap()
            .put("xmi", new XMIResourceFactoryImpl());
        // Since we are creating a stirng, not actually persisting to a file, we will use a
        // "dummy" uri, just make sure it uses the correct extension
        URI fileURI = URI.createURI("audit.xmi");
        Resource poResource = resourceSet.createResource(fileURI);
        poResource.getContents().add(audit);
        // Save to a string writter so we can get the string
        StringWriter outputWriter = new StringWriter();
        try {
            poResource.save(outputWriter, null);
        } catch (IOException e) {
            throw new NeoflexException(e);
        }
        return outputWriter.toString();
    }
    

    There is a huge caveat to this approach though: AFAIK EMF does not support persistance of partial models. As a result, you might get errors if your Audit object is not the root of a complete model. That is, all the non-containment references in objects in the Audit containment hierarchy are to objects in said hierarchy.

    If Audit is not the root the reason for the possible errors is the containment semantics of EMF. If the Audit object is not the root, then assing it to the contents of the resource will effectively remove it from its previous containment. A possible workaround is to store the reference and restore it after you have created the XML string, Something in the lines of:

    EObject container = audit.eContainer;
    // Serialize
    audit.eContainer = container;    // Not so easy, you probably need to save the feature that provided the containment too
    

    References outside the audit containment tree are harder. You probably would need to look into creating you own XMI reasource factory that can handle partial models.