fr.openwide.core.wicket.more.model.GenericEntityModel.java Source code

Java tutorial

Introduction

Here is the source code for fr.openwide.core.wicket.more.model.GenericEntityModel.java

Source

/*
 * Copyright (C) 2009-2011 Open Wide
 * Contact: contact@openwide.fr
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package fr.openwide.core.wicket.more.model;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.wicket.injection.Injector;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;

import fr.openwide.core.jpa.business.generic.model.GenericEntity;
import fr.openwide.core.jpa.business.generic.model.GenericEntityReference;
import fr.openwide.core.jpa.business.generic.service.IEntityService;
import fr.openwide.core.jpa.util.HibernateUtils;
import fr.openwide.core.wicket.more.util.model.LoadableDetachableModelExtendedDebugInformation;

public class GenericEntityModel<K extends Serializable & Comparable<K>, E extends GenericEntity<K, ?>>
        extends LoadableDetachableModel<E> {

    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LoggerFactory.getLogger(GenericEntityModel.class);

    private static final boolean EXTENDED_DEBUG_INFO;
    static {
        EXTENDED_DEBUG_INFO = LOGGER.isDebugEnabled();
        if (EXTENDED_DEBUG_INFO) {
            LOGGER.warn("Extended debug info for GenericEntityModel is enabled."
                    + " This may cause a significant performance hit.");
        }
    }

    @SuppressWarnings("unchecked") // SerializableModelFactory works for any T extending GenericEntity<?, ?>
    public static <E extends GenericEntity<?, ?>> Function<E, GenericEntityModel<?, E>> factory() {
        return (Function<E, GenericEntityModel<?, E>>) (Object) Factory.INSTANCE;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" }) // SerializableModelFactory works for any T extending Serializable
    private enum Factory implements Function<GenericEntity, GenericEntityModel> {
        INSTANCE;

        @Override
        public GenericEntityModel apply(GenericEntity input) {
            return new GenericEntityModel(input);
        }
    }

    @SpringBean
    private IEntityService entityService;

    private transient boolean attached = false;

    private transient LoadableDetachableModelExtendedDebugInformation extendedDebugInformation;

    private GenericEntityReference<K, E> persistedEntityReference;

    /**
     * L'objectif est ici de stocker les entits qui n'ont pas encore t persistes en base (typiquement, quand
     * on fait la cration).
     */
    private E notYetPersistedEntity;

    public static <K extends Serializable & Comparable<K>, E extends GenericEntity<K, ?>> GenericEntityModel<K, E> of(
            E entity) {
        return new GenericEntityModel<K, E>(entity);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static <E extends GenericEntity<?, ?>> GenericEntityModel<?, E> ofUnknownIdType(E entity) {
        return (GenericEntityModel<?, E>) new GenericEntityModel(entity);
    }

    /**
     * Construit un GenericEntityModel dont l'objet est <code>null</code> et qui n'est pas attach.
     */
    public GenericEntityModel() {
        Injector.get().inject(this);
        if (EXTENDED_DEBUG_INFO) {
            this.extendedDebugInformation = new LoadableDetachableModelExtendedDebugInformation();
        } else {
            this.extendedDebugInformation = null;
        }
    }

    public GenericEntityModel(E entity) {
        this();

        setObject(entity);
    }

    @Override
    protected E load() {
        E result = null;
        if (persistedEntityReference != null) {
            result = HibernateUtils.unwrap(entityService.getEntity(persistedEntityReference));
        } else {
            result = notYetPersistedEntity;
        }
        attached = true;
        if (EXTENDED_DEBUG_INFO) {
            extendedDebugInformation.onAttach();
        }
        return result;
    }

    @Override
    public void setObject(E entity) {
        E persistentObject = HibernateUtils.unwrap(entity);
        super.setObject(persistentObject);
        attached = true;
        if (EXTENDED_DEBUG_INFO) {
            extendedDebugInformation.onAttach();
        }
        updateSerializableData(); // Useful to keep equals() and getId() up-to-date and for compatibility with old applications
    }

    protected K getId() {
        return persistedEntityReference != null ? persistedEntityReference.getId() : null;
    }

    @Override
    public void detach() {
        if (EXTENDED_DEBUG_INFO) {
            extendedDebugInformation.onDetach();
        }
        if (!attached) {
            fixSerializableData();
            return;
        }
        updateSerializableData();
        super.detach();
        attached = false;
    }

    /**
     * Updates the serializable data (id, clazz, notYetPersistedEntity) according to the attached object's value.
     * <p>Only called when the model is attached.
     */
    private void updateSerializableData() {
        E entity = super.getObject();
        if (entity != null) {
            if (entity.getId() != null) {
                persistedEntityReference = GenericEntityReference.of(entity);
                notYetPersistedEntity = null;
            } else {
                persistedEntityReference = null;
                notYetPersistedEntity = entity;
            }
        } else {
            persistedEntityReference = null;
            notYetPersistedEntity = null;
        }
    }

    /**
     * If the entity has been persisted since this model has been detached, then fix the serializable data
     * (this may happen if two models reference the same non-persisted entity, for instance)
     */
    private void fixSerializableData() {
        if (notYetPersistedEntity != null && notYetPersistedEntity.getId() != null) {
            persistedEntityReference = GenericEntityReference.of(notYetPersistedEntity);
            notYetPersistedEntity = null;
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof GenericEntityModel)) {
            return false;
        }
        GenericEntityModel<?, ?> other = (GenericEntityModel<?, ?>) obj;
        return new EqualsBuilder().append(persistedEntityReference, other.persistedEntityReference)
                .append(notYetPersistedEntity, other.notYetPersistedEntity).isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(persistedEntityReference).append(notYetPersistedEntity).toHashCode();
    }

    private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
        in.defaultReadObject();
        if (EXTENDED_DEBUG_INFO) {
            this.extendedDebugInformation = new LoadableDetachableModelExtendedDebugInformation();
        }
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        if (attached) {
            LOGGER.warn(
                    "Serializing an attached GenericEntityModel with persistedEntityReference={} and notYetPersistedEntity={}",
                    persistedEntityReference, notYetPersistedEntity);
            if (EXTENDED_DEBUG_INFO) {
                LOGGER.debug("StackTrace from the latest attach (setObject() or load()): \n{}",
                        extendedDebugInformation.getLatestAttachInformation());
            }
        }
        out.defaultWriteObject();
    }

}