/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.isis.objectstore.jdo.applib.service;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.isis.applib.annotation.Action;
import org.apache.isis.applib.annotation.ActionLayout;
import org.apache.isis.applib.annotation.DomainObject;
import org.apache.isis.applib.annotation.DomainObjectLayout;
import org.apache.isis.applib.annotation.Editing;
import org.apache.isis.applib.annotation.MemberGroupLayout;
import org.apache.isis.applib.annotation.MemberOrder;
import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.annotation.Property;
import org.apache.isis.applib.annotation.PropertyLayout;
import org.apache.isis.applib.annotation.SemanticsOf;
import org.apache.isis.applib.annotation.Where;
import org.apache.isis.applib.services.bookmark.Bookmark;
import org.apache.isis.applib.services.bookmark.BookmarkService2;
import org.apache.isis.applib.services.message.MessageService;
import org.apache.isis.applib.services.metamodel.MetaModelService2;
import org.apache.isis.applib.services.metamodel.MetaModelService3;
import org.apache.isis.applib.services.publish.PublisherService;
import org.apache.isis.applib.services.publish.PublishingService;
import org.apache.isis.applib.util.ObjectContracts;
import static org.apache.isis.applib.annotation.Optionality.MANDATORY;
import static org.apache.isis.applib.annotation.Optionality.OPTIONAL;
/**
* An abstraction of some sort of recorded change to a domain object, either a <tt>CommandJdo</tt>, an
* <tt>AuditEntryJdo</tt> or a <tt>PublishedEventJdo</tt>.
*/
@MemberGroupLayout(
columnSpans={6,0,6,12},
left={"Identifiers"},
right={"Target","Detail"})
@DomainObjectLayout(
named="Domain Change"
)
@DomainObject(
editing = Editing.DISABLED
)
public abstract class DomainChangeJdoAbstract {
@SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(DomainChangeJdoAbstract.class);
public static enum ChangeType {
COMMAND,
AUDIT_ENTRY,
/**
* As per {@link PublishingService}.
*
* @deprecated - replaced by {@link #PUBLISHED_INTERACTION} (because {@link PublishingService} has been replaced by {@link PublisherService}).
*/
@Deprecated
PUBLISHED_EVENT,
/**
* As per {@link PublisherService}.
*/
PUBLISHED_INTERACTION;
@Override
public String toString() {
return name().replace("_", " ");
}
}
public DomainChangeJdoAbstract(final ChangeType changeType) {
this.type = changeType;
}
private final ChangeType type;
/**
* Distinguishes commands from audit entries from published events/interactions (when these are shown mixed together in a (standalone) table).
*/
@Property
@PropertyLayout(
hidden = Where.ALL_EXCEPT_STANDALONE_TABLES
)
@MemberOrder(name="Identifiers", sequence = "1")
public ChangeType getType() {
return type;
}
// //////////////////////////////////////
/**
* The user that caused the change.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property
@MemberOrder(name="Identifiers", sequence = "10")
public String getUser() {
return null;
}
// //////////////////////////////////////
/**
* The time that the change occurred.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property
@MemberOrder(name="Identifiers", sequence = "20")
public Timestamp getTimestamp() {
return null;
}
// //////////////////////////////////////
/**
* The unique identifier (a GUID) of the transaction in which this change occurred.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property
@MemberOrder(name="Identifiers",sequence = "50")
public UUID getTransactionId() {
return null;
}
// //////////////////////////////////////
/**
* The class of the domain object being changed.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property
@PropertyLayout(named="Class")
@MemberOrder(name="Target", sequence = "10")
public String getTargetClass() {
return null;
}
// //////////////////////////////////////
@Programmatic
public Bookmark getTarget() {
final String str = getTargetStr();
return str != null? new Bookmark(str): null;
}
@Programmatic
public void setTarget(Bookmark target) {
final String targetStr = target != null ? target.toString() : null;
setTargetStr(targetStr);
}
// //////////////////////////////////////
private String targetAction;
/**
* The member interaction (ie action invocation or property edit) which caused the domain object to be changed.
*
* <p>
* Populated for commands and for published events that represent action invocations or property edits.
* </p>
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
* </p>
*
* <p>
* NB: commands and published events applied only to actions, hence the name of this field. In a future release
* the name of this field may change to "TargetMember". Note that the {@link PropertyLayout} already uses
* "Member" this as a name hint.
* </p>
*
*/
@Property(
optionality = OPTIONAL
)
@PropertyLayout(
named="Member",
hidden = Where.ALL_EXCEPT_STANDALONE_TABLES
)
@MemberOrder(name="Target", sequence = "20")
public String getTargetAction() {
return targetAction;
}
// //////////////////////////////////////
/**
* The (string representation of the) {@link Bookmark} identifying the domain object that has changed.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property
@PropertyLayout(named="Object")
@MemberOrder(name="Target", sequence="30")
public String getTargetStr() {
return null;
}
/**
* For {@link #setTarget(Bookmark)} to delegate to.
*/
public abstract void setTargetStr(final String targetStr);
// //////////////////////////////////////
@Action(
semantics = SemanticsOf.SAFE
)
@ActionLayout(
named = "Open"
)
@MemberOrder(name="TargetStr", sequence="1")
public Object openTargetObject() {
try {
return bookmarkService != null
? bookmarkService.lookup(getTarget(), BookmarkService2.FieldResetPolicy.DONT_RESET)
: null;
} catch(RuntimeException ex) {
if(ex.getClass().getName().contains("ObjectNotFoundException")) {
messageService.warnUser("Object not found - has it since been deleted?");
return null;
}
throw ex;
}
}
public boolean hideOpenTargetObject() {
return getTarget() == null;
}
public String disableOpenTargetObject() {
final Object targetObject = getTarget();
if (targetObject == null) {
return null;
}
final MetaModelService2.Sort sortOfObject = metaModelService.sortOf(getTarget(), MetaModelService3.Mode.RELAXED);
return !(sortOfObject.isViewModel() || sortOfObject.isJdoEntity())
? "Can only open view models or entities"
: null;
}
// //////////////////////////////////////
/**
* The property of the object that was changed.
*
* <p>
* Populated only for <tt>AuditEntryJdo</tt>.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property(
optionality = OPTIONAL
)
@PropertyLayout(
hidden = Where.ALL_EXCEPT_STANDALONE_TABLES
)
@MemberOrder(name="Target",sequence = "21")
public String getPropertyId() {
return null;
}
// //////////////////////////////////////
/**
* The value of the property prior to it being changed.
*
* <p>
* Populated only for <tt>AuditEntryJdo</tt>.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property(
optionality = OPTIONAL
)
@PropertyLayout(
hidden = Where.ALL_EXCEPT_STANDALONE_TABLES
)
@MemberOrder(name="Detail",sequence = "6")
public String getPreValue() {
return null;
}
// //////////////////////////////////////
/**
* The value of the property after it has changed.
*
* <p>
* Populated only for <tt>AuditEntryJdo</tt>.
*
* <p>
* This dummy implementation is a trick so that Isis will render the property in a standalone table. Each of the
* subclasses override with the "real" implementation.
*/
@Property(
optionality = MANDATORY
)
@PropertyLayout(
hidden = Where.ALL_EXCEPT_STANDALONE_TABLES
)
@MemberOrder(name="Detail",sequence = "7")
public String getPostValue() {
return null;
}
// //////////////////////////////////////
public static Comparator<DomainChangeJdoAbstract> compareByTimestampDescThenType(){
return ObjectContracts.compareBy("timestamp desc","type");
}
public static Comparator<DomainChangeJdoAbstract> compareByTargetThenTimestampDescThenType(){
return ObjectContracts.compareBy("targetStr","timestamp desc","type");
}
public static Comparator<DomainChangeJdoAbstract> compareByTargetThenUserThenTimestampDescThenType(){
return ObjectContracts.compareBy("targetStr","user","timestamp desc","type");
}
public static Comparator<DomainChangeJdoAbstract> compareByUserThenTimestampDescThenType(){
return ObjectContracts.compareBy("user","timestamp desc","type");
}
public static Comparator<DomainChangeJdoAbstract> compareByUserThenTargetThenTimestampDescThenType(){
return ObjectContracts.compareBy("user","targetStr","timestamp desc","type");
}
// //////////////////////////////////////
// dependencies
// //////////////////////////////////////
@javax.inject.Inject
protected BookmarkService2 bookmarkService;
@javax.inject.Inject
protected MessageService messageService;
@javax.inject.Inject
protected MetaModelService3 metaModelService;
}