package fr.openwide.core.wicket.more.model.threadsafe; import java.io.Serializable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.wicket.Session; import org.apache.wicket.injection.Injector; import org.apache.wicket.spring.injection.annot.SpringBean; 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.model.GenericEntityModel; /** * An alternative implementation of {@link GenericEntityModel} that is thread-safe, and may thus be used in multiple request cycles at the same time. * <p>This class should be used when entity models are needed in a global object, such as the {@link Session wicket session}. * * @see SessionThreadSafeDerivedSerializableStateLoadableDetachableModel */ public class SessionThreadSafeGenericEntityModel<K extends Serializable & Comparable<K>, E extends GenericEntity<K, ?>> extends SessionThreadSafeDerivedSerializableStateLoadableDetachableModel<E, SessionThreadSafeGenericEntityModel.SerializableState<K, E>> { private static final long serialVersionUID = 1L; @SpringBean private IEntityService entityService; public SessionThreadSafeGenericEntityModel() { super(); Injector.get().inject(this); } public SessionThreadSafeGenericEntityModel(E object) { super(object); Injector.get().inject(this); } @Override protected E load(SerializableState<K, E> serializableState) { if (serializableState == null) { return null; } GenericEntityReference<K, E> persistedEntityReference = serializableState.persistedEntityReference; if (persistedEntityReference != null) { return entityService.getEntity(persistedEntityReference); } else { return serializableState.notYetPersistedEntity; } } @Override protected SerializableState<K, E> makeSerializable(E currentObject) { return new SerializableState<K, E>(currentObject); } @Override protected SerializableState<K, E> normalizeDetached(SerializableState<K, E> current) { return current == null ? null : current.normalize(); } /** * Immutable, serializable state for detached models. */ protected static class SerializableState<K extends Serializable & Comparable<K>, E extends GenericEntity<K, ?>> implements Serializable { private static final long serialVersionUID = 1L; private final GenericEntityReference<K, E> persistedEntityReference; /** * Here we want to serialize entities that haven't been persisted to the database yet. * <p>This typically happens when the user is in the process of creating the entity. */ private final E notYetPersistedEntity; public SerializableState(E entity) { E unwrapped = HibernateUtils.unwrap(entity); if (unwrapped != null) { if (unwrapped.getId() != null) { persistedEntityReference = GenericEntityReference.of(unwrapped); notYetPersistedEntity = null; } else { persistedEntityReference = null; notYetPersistedEntity = unwrapped; } } else { persistedEntityReference = null; notYetPersistedEntity = null; } } public SerializableState<K, E> normalize() { if (notYetPersistedEntity == null || notYetPersistedEntity.getId() == null) { return this; } else { return new SerializableState<>(notYetPersistedEntity); } } @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof SerializableState)) { return false; } SerializableState<K, E> other = (SerializableState<K, E>) obj; return new EqualsBuilder() .append(persistedEntityReference, other.persistedEntityReference) .append(notYetPersistedEntity, other.notYetPersistedEntity) .build(); } @Override public int hashCode() { return new HashCodeBuilder() .append(persistedEntityReference) .append(notYetPersistedEntity) .toHashCode(); } } }