Example usage for org.hibernate.type EmbeddedComponentType getSubtypes

List of usage examples for org.hibernate.type EmbeddedComponentType getSubtypes

Introduction

In this page you can find the example usage for org.hibernate.type EmbeddedComponentType getSubtypes.

Prototype

@Override
    public Type[] getSubtypes() 

Source Link

Usage

From source file:org.openmrs.module.sync.api.db.hibernate.HibernateSyncInterceptor.java

License:Open Source License

/**
 * Serializes and packages an intercepted change in object state.
 * <p>//from   ww w. ja  v a2 s .com
 * IMPORTANT serialization notes:
 * <p>
 * Transient Properties. Transients are not serialized/journalled. Marking an object property as
 * transient is the supported way of designating it as something not to be recorded into the
 * journal.
 * <p/>
 * Hibernate Identity property. A property designated in Hibernate as identity (i.e. primary
 * key) *is* not serialized. This is because sync does not enforce global uniqueness of database
 * primary keys. Instead, custom uuid property is used. This allows us to continue to use native
 * types for 'traditional' entity relationships.
 *
 * @param entity The object changed.
 * @param currentState Array containing data for each field in the object as they will be saved.
 * @param propertyNames Array containing name for each field in the object, corresponding to
 *            currentState.
 * @param types Array containing Type of the field in the object, corresponding to currentState.
 * @param state SyncItemState, e.g. NEW, UPDATED, DELETED
 * @param id Value of the identifier for this entity
 */
protected void packageObject(OpenmrsObject entity, Object[] currentState, String[] propertyNames, Type[] types,
        Serializable id, SyncItemState state) throws SyncException {

    String objectUuid = null;
    String originalRecordUuid = null;
    Set<String> transientProps = null;
    String infoMsg = null;

    ClassMetadata data = null;
    String idPropertyName = null;
    org.hibernate.tuple.IdentifierProperty idPropertyObj = null;

    // The container of values to be serialized:
    // Holds tuples of <property-name> -> {<property-type-name>,
    // <property-value as string>}
    HashMap<String, PropertyClassValue> values = new HashMap<String, PropertyClassValue>();

    try {
        objectUuid = entity.getUuid();

        // pull-out sync-network wide change id for the sync *record* (not the entity itself),
        // if one was already assigned (i.e. this change is coming from some other server)
        originalRecordUuid = getSyncRecord().getOriginalUuid();

        if (log.isDebugEnabled()) {
            // build up a starting msg for all logging:
            StringBuilder sb = new StringBuilder();
            sb.append("In PackageObject, entity type:");
            sb.append(entity.getClass().getName());
            sb.append(", entity uuid:");
            sb.append(objectUuid);
            sb.append(", originalUuid uuid:");
            sb.append(originalRecordUuid);
            log.debug(sb.toString());
        }

        // Transient properties are not serialized.
        transientProps = new HashSet<String>();
        for (Field f : entity.getClass().getDeclaredFields()) {
            if (Modifier.isTransient(f.getModifiers())) {
                transientProps.add(f.getName());
                if (log.isDebugEnabled())
                    log.debug("The field " + f.getName() + " is transient - so we won't serialize it");
            }
        }

        /*
         * Retrieve metadata for this type; we need to determine what is the
         * PK field for this type. We need to know this since PK values are
         * *not* journalled; values of primary keys are assigned where
         * physical DB records are created. This is so to avoid issues with
         * id collisions.
         *
         * In case of <generator class="assigned" />, the Identifier
         * property is already assigned value and needs to be journalled.
         * Also, the prop will *not* be part of currentState,thus we need to
         * pull it out with reflection/metadata.
         */
        data = getSessionFactory().getClassMetadata(entity.getClass());
        if (data.hasIdentifierProperty()) {
            idPropertyName = data.getIdentifierPropertyName();
            idPropertyObj = ((org.hibernate.persister.entity.AbstractEntityPersister) data).getEntityMetamodel()
                    .getIdentifierProperty();

            if (id != null && idPropertyObj.getIdentifierGenerator() != null
                    && (idPropertyObj.getIdentifierGenerator() instanceof org.hibernate.id.Assigned
                    //   || idPropertyObj.getIdentifierGenerator() instanceof org.openmrs.api.db.hibernate.NativeIfNotAssignedIdentityGenerator
                    )) {
                // serialize value as string
                values.put(idPropertyName, new PropertyClassValue(id.getClass().getName(), id.toString()));
            }
        } else if (data.getIdentifierType() instanceof EmbeddedComponentType) {
            // if we have a component identifier type (like AlertRecipient),
            // make
            // sure we include those properties
            EmbeddedComponentType type = (EmbeddedComponentType) data.getIdentifierType();
            for (int i = 0; i < type.getPropertyNames().length; i++) {
                String propertyName = type.getPropertyNames()[i];
                Object propertyValue = type.getPropertyValue(entity, i, org.hibernate.EntityMode.POJO);
                addProperty(values, entity, type.getSubtypes()[i], propertyName, propertyValue, infoMsg);
            }
        }

        /*
         * Loop through all the properties/values and put in a hash for
         * duplicate removal
         */
        for (int i = 0; i < types.length; i++) {
            String typeName = types[i].getName();
            if (log.isDebugEnabled())
                log.debug("Processing, type: " + typeName + " Field: " + propertyNames[i]);

            if (propertyNames[i].equals(idPropertyName) && log.isInfoEnabled())
                log.debug(infoMsg + ", Id for this class: " + idPropertyName + " , value:" + currentState[i]);

            if (currentState[i] != null) {
                // is this the primary key or transient? if so, we don't
                // want to serialize
                if (propertyNames[i].equals(idPropertyName)
                        || ("personId".equals(idPropertyName) && "patientId".equals(propertyNames[i]))
                        //|| ("personId".equals(idPropertyName) && "userId".equals(propertyNames[i]))
                        || transientProps.contains(propertyNames[i])) {
                    // if (log.isInfoEnabled())
                    log.debug("Skipping property (" + propertyNames[i]
                            + ") because it's either the primary key or it's transient.");

                } else {

                    addProperty(values, entity, types[i], propertyNames[i], currentState[i], infoMsg);
                }
            } else {
                // current state null -- skip
                if (log.isDebugEnabled())
                    log.debug("Field Type: " + typeName + " Field Name: " + propertyNames[i]
                            + " is null, skipped");
            }
        }

        /*
         * Now serialize the data identified and put in the value-map
         */
        // Setup the serialization data structures to hold the state
        Package pkg = new Package();
        String className = entity.getClass().getName();
        Record xml = pkg.createRecordForWrite(className);
        Item entityItem = xml.getRootItem();

        // loop through the map of the properties that need to be serialized
        for (Map.Entry<String, PropertyClassValue> me : values.entrySet()) {
            String property = me.getKey();

            // if we are processing onDelete event all we need is uuid
            if ((state == SyncItemState.DELETED) && (!"uuid".equals(property))) {
                continue;
            }

            try {
                PropertyClassValue pcv = me.getValue();
                appendRecord(xml, entity, entityItem, property, pcv.getClazz(), pcv.getValue());
            } catch (Exception e) {
                String msg = "Could not append attribute. Error while processing property: " + property + " - "
                        + e.getMessage();
                throw (new SyncException(msg, e));
            }
        }

        values.clear(); // Be nice to GC

        if (objectUuid == null)
            throw new SyncException("uuid is null for: " + className + " with id: " + id);

        /*
         * Create SyncItem and store change in SyncRecord kept in
         * ThreadLocal.
         */
        SyncItem syncItem = new SyncItem();
        syncItem.setKey(new SyncItemKey<String>(objectUuid, String.class));
        syncItem.setState(state);
        syncItem.setContent(xml.toStringAsDocumentFragement());
        syncItem.setContainedType(entity.getClass());

        if (log.isDebugEnabled())
            log.debug("Adding SyncItem to SyncRecord");

        getSyncRecord().addItem(syncItem);
        getSyncRecord().addContainedClass(entity.getClass().getName());

        // set the originating uuid for the record: do this once per Tx;
        // else we may end up with empty string
        if (getSyncRecord().getOriginalUuid() == null || "".equals(getSyncRecord().getOriginalUuid())) {
            getSyncRecord().setOriginalUuid(originalRecordUuid);
        }
    } catch (SyncException ex) {
        log.error("Journal error\n", ex);
        throw (ex);
    } catch (Exception e) {
        log.error("Journal error\n", e);
        throw (new SyncException("Error in interceptor, see log messages and callstack.", e));
    }

    return;
}