http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java b/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java deleted file mode 100644 index 75f5583..0000000 --- a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java +++ /dev/null @@ -1,979 +0,0 @@ -/* - * 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 dom.todo; - -import java.math.BigDecimal; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import javax.jdo.JDOHelper; -import javax.jdo.annotations.IdentityType; -import javax.jdo.annotations.VersionStrategy; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; -import com.google.common.collect.Ordering; -import org.joda.time.LocalDate; -import org.apache.isis.applib.DomainObjectContainer; -import org.apache.isis.applib.Identifier; -import org.apache.isis.applib.NonRecoverableException; -import org.apache.isis.applib.RecoverableException; -import org.apache.isis.applib.annotation.Action; -import org.apache.isis.applib.annotation.BookmarkPolicy; -import org.apache.isis.applib.annotation.Collection; -import org.apache.isis.applib.annotation.CollectionLayout; -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.RestrictTo; -import org.apache.isis.applib.annotation.InvokeOn; -import org.apache.isis.applib.annotation.InvokedOn; -import org.apache.isis.applib.annotation.Optionality; -import org.apache.isis.applib.annotation.Parameter; -import org.apache.isis.applib.annotation.ParameterLayout; -import org.apache.isis.applib.annotation.Programmatic; -import org.apache.isis.applib.annotation.Property; -import org.apache.isis.applib.annotation.SemanticsOf; -import org.apache.isis.applib.annotation.Where; -import org.apache.isis.applib.security.UserMemento; -import org.apache.isis.applib.services.actinvoc.ActionInvocationContext; -import org.apache.isis.applib.services.eventbus.ActionDomainEvent; -import org.apache.isis.applib.services.eventbus.EventBusService; -import org.apache.isis.applib.services.scratchpad.Scratchpad; -import org.apache.isis.applib.services.wrapper.HiddenException; -import org.apache.isis.applib.services.wrapper.WrapperFactory; -import org.apache.isis.applib.util.ObjectContracts; -import org.apache.isis.applib.util.TitleBuffer; -import org.apache.isis.applib.value.Blob; -import org.apache.isis.applib.value.Clob; - [email protected](identityType=IdentityType.DATASTORE) [email protected]( - strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY, - column="id") [email protected]( - strategy=VersionStrategy.VERSION_NUMBER, - column="version") [email protected]({ - @javax.jdo.annotations.Unique( - name="ToDoItem_description_must_be_unique", - members={"ownedBy","description"}) -}) [email protected]( { - @javax.jdo.annotations.Query( - name = "findByOwnedBy", language = "JDOQL", - value = "SELECT " - + "FROM dom.todo.ToDoItem " - + "WHERE ownedBy == :ownedBy"), - @javax.jdo.annotations.Query( - name = "findByOwnedByAndCompleteIsFalse", language = "JDOQL", - value = "SELECT " - + "FROM dom.todo.ToDoItem " - + "WHERE ownedBy == :ownedBy " - + " && complete == false"), - @javax.jdo.annotations.Query( - name = "findByOwnedByAndCompleteIsTrue", language = "JDOQL", - value = "SELECT " - + "FROM dom.todo.ToDoItem " - + "WHERE ownedBy == :ownedBy " - + "&& complete == true"), - @javax.jdo.annotations.Query( - name = "findByOwnedByAndCategory", language = "JDOQL", - value = "SELECT " - + "FROM dom.todo.ToDoItem " - + "WHERE ownedBy == :ownedBy " - + "&& category == :category"), - @javax.jdo.annotations.Query( - name = "findByOwnedByAndDescriptionContains", language = "JDOQL", - value = "SELECT " - + "FROM dom.todo.ToDoItem " - + "WHERE ownedBy == :ownedBy && " - + "description.indexOf(:description) >= 0") -}) -@DomainObject( - autoCompleteRepository = ToDoItems.class, // for drop-downs, unless autoCompleteNXxx() is present - autoCompleteAction = "autoComplete", - // bounded = true, // for drop-downs if only a small number of instances only (overrides autoComplete) - objectType = "TODO" -) -@DomainObjectLayout( - bookmarking = BookmarkPolicy.AS_ROOT -) -public class ToDoItem implements Categorized, Comparable<ToDoItem> { - - //region > LOG - /** - * It isn't common for entities to log, but they can if required. - * Isis uses slf4j API internally (with log4j as implementation), and is the recommended API to use. - */ - private final static org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(ToDoItem.class); - //endregion - - // region > title, icon - public String title() { - final TitleBuffer buf = new TitleBuffer(); - buf.append(getDescription()); - if (isComplete()) { - buf.append("- Completed!"); - } else { - try { - final LocalDate dueBy = wrapperFactory.wrap(this).getDueBy(); - if (dueBy != null) { - buf.append(" due by", dueBy); - } - } catch(final HiddenException ignored) { - } - } - return buf.toString(); - } - - public String iconName() { - return !isComplete() ? "todo" : "done"; - } - - public String cssClass() { return iconName(); } - - //endregion - - //region > description (property) - private String description; - - @javax.jdo.annotations.Column(allowsNull="false", length=100) - @Property( - regexPattern = "\\w[@&:\\-\\,\\.\\+ \\w]*" - ) - public String getDescription() { - return description; - } - - public void setDescription(final String description) { - this.description = description; - } - public void modifyDescription(final String description) { - setDescription(description); - } - public void clearDescription() { - setDescription(null); - } - //endregion - - //region > dueBy (property) - @javax.jdo.annotations.Persistent(defaultFetchGroup="true") - private LocalDate dueBy; - - @javax.jdo.annotations.Column(allowsNull="true") - public LocalDate getDueBy() { - return dueBy; - } - - /** - * Demonstrates how to perform security checks within the domain code. - * - * <p> - * Generally speaking this approach is not recommended; such checks should - * wherever possible be externalized in the security subsystem. - * </p> - */ - public boolean hideDueBy() { - final UserMemento user = container.getUser(); - return user.hasRole("realm1:noDueBy_role"); - } - - public void setDueBy(final LocalDate dueBy) { - this.dueBy = dueBy; - } - public void clearDueBy() { - setDueBy(null); - } - // proposed new value is validated before setting - public String validateDueBy(final LocalDate dueBy) { - if (dueBy == null) { - return null; - } - return toDoItems.validateDueBy(dueBy); - } - //endregion - - //region > category and subcategory (property) - - public static enum Category { - Professional { - @Override - public List<Subcategory> subcategories() { - return Arrays.asList(null, Subcategory.OpenSource, Subcategory.Consulting, Subcategory.Education, Subcategory.Marketing); - } - }, Domestic { - @Override - public List<Subcategory> subcategories() { - return Arrays.asList(null, Subcategory.Shopping, Subcategory.Housework, Subcategory.Garden, Subcategory.Chores); - } - }, Other { - @Override - public List<Subcategory> subcategories() { - return Arrays.asList(null, Subcategory.Other); - } - }; - - public abstract List<Subcategory> subcategories(); - } - - public static enum Subcategory { - // professional - OpenSource, Consulting, Education, Marketing, - // domestic - Shopping, Housework, Garden, Chores, - // other - Other; - - public static List<Subcategory> listFor(final Category category) { - return category != null? category.subcategories(): Collections.<Subcategory>emptyList(); - } - - static String validate(final Category category, final Subcategory subcategory) { - if(category == null) { - return "Enter category first"; - } - return !category.subcategories().contains(subcategory) - ? "Invalid subcategory for category '" + category + "'" - : null; - } - - public static Predicate<Subcategory> thoseFor(final Category category) { - return new Predicate<Subcategory>() { - - @Override - public boolean apply(final Subcategory subcategory) { - return category.subcategories().contains(subcategory); - } - }; - } - } - - // ////////////////////////////////////// - - private Category category; - - @javax.jdo.annotations.Column(allowsNull="false") - @Property( - editing = Editing.DISABLED, - editingDisabledReason = "Use action to update both category and subcategory" - ) - public Category getCategory() { - return category; - } - - public void setCategory(final Category category) { - this.category = category; - } - - // ////////////////////////////////////// - - private Subcategory subcategory; - - @javax.jdo.annotations.Column(allowsNull="true") - @Property( - editing = Editing.DISABLED, - editingDisabledReason = "Use action to update both category and subcategory" - ) - public Subcategory getSubcategory() { - return subcategory; - } - public void setSubcategory(final Subcategory subcategory) { - this.subcategory = subcategory; - } - //endregion - - //region > ownedBy (property) - - private String ownedBy; - - @javax.jdo.annotations.Column(allowsNull="false") - public String getOwnedBy() { - return ownedBy; - } - - public void setOwnedBy(final String ownedBy) { - this.ownedBy = ownedBy; - } - //endregion - - //region > complete (property), completed (action), notYetCompleted (action) - - private boolean complete; - - @Property( - editing = Editing.DISABLED - ) - public boolean isComplete() { - return complete; - } - - public void setComplete(final boolean complete) { - this.complete = complete; - } - - @Action( - domainEvent =CompletedEvent.class, - invokeOn = InvokeOn.OBJECT_AND_COLLECTION - ) - public ToDoItem completed() { - setComplete(true); - - // - // remainder of method just demonstrates the use of the Bulk.InteractionContext service - // - @SuppressWarnings("unused") - final List<Object> allObjects = actionInvocationContext.getDomainObjects(); - - LOG.debug("completed: " - + actionInvocationContext.getIndex() + - " [" + actionInvocationContext.getSize() + "]" - + (actionInvocationContext.isFirst() ? " (first)" : "") - + (actionInvocationContext.isLast() ? " (last)" : "")); - - // if invoked as a regular action, return this object; - // otherwise (if invoked as bulk), return null (so go back to the list) - return actionInvocationContext.getInvokedOn() == InvokedOn.OBJECT? this: null; - } - // disable action dependent on state of object - public String disableCompleted() { - return isComplete() ? "Already completed" : null; - } - - @Action( - invokeOn = InvokeOn.OBJECT_AND_COLLECTION - ) - public ToDoItem notYetCompleted() { - setComplete(false); - - // if invoked as a regular action, return this object; - // otherwise (if invoked as bulk), return null (so go back to the list) - return actionInvocationContext.getInvokedOn() == InvokedOn.OBJECT ? this: null; - } - // disable action dependent on state of object - public String disableNotYetCompleted() { - return !complete ? "Not yet completed" : null; - } - //endregion - - //region > completeSlowly (property) - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - @Action( - hidden = Where.EVERYWHERE - ) - public void completeSlowly(final int millis) { - try { - Thread.sleep(millis); - } catch (final InterruptedException ignored) { - } - setComplete(true); - } - //endregion - - //region > cost (property), updateCost (action) - private BigDecimal cost; - - @javax.jdo.annotations.Column(allowsNull="true", scale=2) - @javax.validation.constraints.Digits(integer=10, fraction=2) - @Property( - editing = Editing.DISABLED, - editingDisabledReason = "Update using action" - ) - public BigDecimal getCost() { - return cost; - } - - public void setCost(final BigDecimal cost) { - this.cost = cost!=null?cost.setScale(2, BigDecimal.ROUND_HALF_EVEN):null; - } - - @Action( - semantics = SemanticsOf.IDEMPOTENT - ) - public ToDoItem updateCost( - @Parameter(optionality = Optionality.OPTIONAL) - @ParameterLayout(named = "New cost") - @javax.validation.constraints.Digits(integer=10, fraction=2) - final BigDecimal cost) { - LOG.debug("%s: cost updated: %s -> %s", container.titleOf(this), getCost(), cost); - - // just to simulate a long-running action - try { - Thread.sleep(3000); - } catch (final InterruptedException ignored) { - } - - setCost(cost); - return this; - } - - // provide a default value for argument #0 - public BigDecimal default0UpdateCost() { - return getCost(); - } - - // validate action arguments - public String validateUpdateCost(final BigDecimal proposedCost) { - if(proposedCost == null) { return null; } - return proposedCost.compareTo(BigDecimal.ZERO) < 0? "Cost must be positive": null; - } - //endregion - - //region > notes (property) - private String notes; - - @javax.jdo.annotations.Column(allowsNull="true", length=400) - public String getNotes() { - return notes; - } - - public void setNotes(final String notes) { - this.notes = notes; - } - //endregion - - //region > attachment (property) - private Blob attachment; - @javax.jdo.annotations.Persistent(defaultFetchGroup="false", columns = { - @javax.jdo.annotations.Column(name = "attachment_name"), - @javax.jdo.annotations.Column(name = "attachment_mimetype"), - @javax.jdo.annotations.Column(name = "attachment_bytes", jdbcType = "BLOB", sqlType = "BLOB") - }) - @Property( - optionality = Optionality.OPTIONAL - ) - public Blob getAttachment() { - return attachment; - } - - public void setAttachment(final Blob attachment) { - this.attachment = attachment; - } - //endregion - - //region > doc (property) - private Clob doc; - @javax.jdo.annotations.Persistent(defaultFetchGroup="false", columns = { - @javax.jdo.annotations.Column(name = "doc_name"), - @javax.jdo.annotations.Column(name = "doc_mimetype"), - @javax.jdo.annotations.Column(name = "doc_chars", jdbcType = "CLOB", sqlType = "CLOB") - }) - @Property( - optionality = Optionality.OPTIONAL - ) - public Clob getDoc() { - return doc; - } - - public void setDoc(final Clob doc) { - this.doc = doc; - } - //endregion - - //region > version (derived property) - public Long getVersionSequence() { - if(!(this instanceof javax.jdo.spi.PersistenceCapable)) { - return null; - } - return (Long) JDOHelper.getVersion((javax.jdo.spi.PersistenceCapable) this); - } - // hide property (imperatively, based on state of object) - public boolean hideVersionSequence() { - return !(this instanceof javax.jdo.spi.PersistenceCapable); - } - //endregion - - //region > dependencies (property), add (action), remove (action) - - // overrides the natural ordering - public static class DependenciesComparator implements Comparator<ToDoItem> { - @Override - public int compare(final ToDoItem p, final ToDoItem q) { - final Ordering<ToDoItem> byDescription = new Ordering<ToDoItem>() { - public int compare(final ToDoItem p, final ToDoItem q) { - return Ordering.natural().nullsFirst().compare(p.getDescription(), q.getDescription()); - } - }; - return byDescription - .compound(Ordering.<ToDoItem>natural()) - .compare(p, q); - } - } - - @javax.jdo.annotations.Persistent(table="ToDoItemDependencies") - @javax.jdo.annotations.Join(column="dependingId") - @javax.jdo.annotations.Element(column="dependentId") - - private Set<ToDoItem> dependencies = new TreeSet<>(); - //private SortedSet<ToDoItem> dependencies = new TreeSet<>(); // not compatible with neo4j (as of DN v3.2.3) - - @Collection() - @CollectionLayout(/*sortedBy = DependenciesComparator.class*/) // not compatible with neo4j (as of DN v3.2.3) - public Set<ToDoItem> getDependencies() { - return dependencies; - } - - public void setDependencies(final Set<ToDoItem> dependencies) { - this.dependencies = dependencies; - } - - public void addToDependencies(final ToDoItem toDoItem) { - getDependencies().add(toDoItem); - } - public void removeFromDependencies(final ToDoItem toDoItem) { - getDependencies().remove(toDoItem); - } - - public ToDoItem add( - @ParameterLayout(typicalLength = 20) - final ToDoItem toDoItem) { - // By wrapping the call, Isis will detect that the collection is modified - // and it will automatically send CollectionInteractionEvents to the Event Bus. - // ToDoItemSubscriptions is a demo subscriber to this event - wrapperFactory.wrapSkipRules(this).addToDependencies(toDoItem); - return this; - } - public List<ToDoItem> autoComplete0Add( - @Parameter(minLength = 2) - final String search) { - final List<ToDoItem> list = toDoItems.autoComplete(search); - list.removeAll(getDependencies()); - list.remove(this); - return list; - } - - public String disableAdd(final ToDoItem toDoItem) { - if(isComplete()) { - return "Cannot add dependencies for items that are complete"; - } - return null; - } - // validate the provided argument prior to invoking action - public String validateAdd(final ToDoItem toDoItem) { - if(getDependencies().contains(toDoItem)) { - return "Already a dependency"; - } - if(toDoItem == this) { - return "Can't set up a dependency to self"; - } - return null; - } - - public ToDoItem remove( - @ParameterLayout(typicalLength = 20) - final ToDoItem toDoItem) { - // By wrapping the call, Isis will detect that the collection is modified - // and it will automatically send a CollectionInteractionEvent to the Event Bus. - // ToDoItemSubscriptions is a demo subscriber to this event - wrapperFactory.wrapSkipRules(this).removeFromDependencies(toDoItem); - return this; - } - // disable action dependent on state of object - public String disableRemove(final ToDoItem toDoItem) { - if(isComplete()) { - return "Cannot remove dependencies for items that are complete"; - } - return getDependencies().isEmpty()? "No dependencies to remove": null; - } - // validate the provided argument prior to invoking action - public String validateRemove(final ToDoItem toDoItem) { - if(!getDependencies().contains(toDoItem)) { - return "Not a dependency"; - } - return null; - } - // provide a drop-down - public java.util.Collection<ToDoItem> choices0Remove() { - return getDependencies(); - } - //endregion - - //region > clone (action) - - // the name of the action in the UI - // nb: method is not called "clone()" is inherited by java.lang.Object and - // (a) has different semantics and (b) is in any case automatically ignored - // by the framework - public ToDoItem duplicate( - @Parameter(regexPattern = "\\w[@&:\\-\\,\\.\\+ \\w]*" ) - @ParameterLayout(named="Description") - final String description, - @ParameterLayout(named="Category") - final Category category, - @ParameterLayout(named="Subcategory") - final Subcategory subcategory, - @Parameter(optionality = Optionality.OPTIONAL) - @ParameterLayout(named="Due by") - final LocalDate dueBy, - @Parameter(optionality = Optionality.OPTIONAL) - @ParameterLayout(named="Cost") - final BigDecimal cost) { - return toDoItems.newToDo(description, category, subcategory, dueBy, cost); - } - public String default0Duplicate() { - return getDescription() + " - Copy"; - } - public Category default1Duplicate() { - return getCategory(); - } - public Subcategory default2Duplicate() { - return getSubcategory(); - } - public LocalDate default3Duplicate() { - return getDueBy(); - } - public List<Subcategory> choices2Duplicate( - final String description, final Category category) { - return toDoItems.choices2NewToDo(description, category); - } - public String validateDuplicate( - final String description, - final Category category, final Subcategory subcategory, - final LocalDate dueBy, final BigDecimal cost) { - return toDoItems.validateNewToDo(description, category, subcategory, dueBy, cost); - } - //endregion - - //region > delete (action) - @Action( - domainEvent =DeletedEvent.class, - invokeOn = InvokeOn.OBJECT_AND_COLLECTION - ) - public List<ToDoItem> delete() { - - container.removeIfNotAlready(this); - - container.informUser("Deleted " + container.titleOf(this)); - - // invalid to return 'this' (cannot render a deleted object) - return toDoItems.notYetComplete(); - } - //endregion - - //region > totalCost (property) - @Action( - semantics = SemanticsOf.SAFE, - invokeOn = InvokeOn.COLLECTION_ONLY - ) - public BigDecimal totalCost() { - BigDecimal total = (BigDecimal) scratchpad.get("runningTotal"); - if(getCost() != null) { - total = total != null ? total.add(getCost()) : getCost(); - scratchpad.put("runningTotal", total); - } - return total.setScale(2, BigDecimal.ROUND_HALF_EVEN); - } - //endregion - - //region > openSourceCodeOnGithub (action) - @Action( - semantics = SemanticsOf.SAFE, - restrictTo = RestrictTo.PROTOTYPING - ) - public URL openSourceCodeOnGithub() throws MalformedURLException { - return new URL("https://github.com/apache/isis/tree/master/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.java"); - } - //endregion - - //region > demoException (action) - - static enum DemoExceptionType { - RecoverableException, - RecoverableExceptionAutoEscalated, - NonRecoverableException; - } - - @Action( - semantics = SemanticsOf.SAFE, - restrictTo = RestrictTo.PROTOTYPING - ) - public void demoException( - @ParameterLayout(named="Type") - final DemoExceptionType type) { - switch(type) { - case NonRecoverableException: - throw new NonRecoverableException("Demo throwing " + type.name()); - case RecoverableException: - throw new RecoverableException("Demo throwing " + type.name()); - case RecoverableExceptionAutoEscalated: - try { - // this will trigger an exception (because category cannot be null), causing the xactn to be aborted - setCategory(null); - container.flush(); - } catch(Exception e) { - // it's a programming mistake to throw only a recoverable exception here, because of the xactn's state. - // the framework should instead auto-escalate this to a non-recoverable exception - throw new RecoverableException("Demo throwing " + type.name(), e); - } - } - } - //endregion - - //region > lifecycle callbacks - - public void created() { - LOG.debug("lifecycle callback: created: " + this.toString()); - } - public void loaded() { - LOG.debug("lifecycle callback: loaded: " + this.toString()); - } - public void persisting() { - LOG.debug("lifecycle callback: persisting: " + this.toString()); - } - public void persisted() { - LOG.debug("lifecycle callback: persisted: " + this.toString()); - } - public void updating() { - LOG.debug("lifecycle callback: updating: " + this.toString()); - } - public void updated() { - LOG.debug("lifecycle callback: updated: " + this.toString()); - } - public void removing() { - LOG.debug("lifecycle callback: removing: " + this.toString()); - } - public void removed() { - LOG.debug("lifecycle callback: removed: " + this.toString()); - } - //endregion - - //region > object-level validation - - /** - * Prevent user from viewing another user's data. - */ - public boolean hidden() { - // uncomment to enable. As things stand, the disabled() method below instead will make object "read-only". - //return !Objects.equal(getOwnedBy(), container.getUser().getName()); - return false; - } - - /** - * Prevent user from modifying any other user's data. - */ - public String disabled(final Identifier.Type identifierType){ - final UserMemento currentUser = container.getUser(); - final String currentUserName = currentUser.getName(); - if(Objects.equal(getOwnedBy(), currentUserName)) { return null; } - return "This object is owned by '" + getOwnedBy() + "' and cannot be modified by you"; - } - - /** - * In a real app, if this were actually a rule, then we'd expect that - * invoking the {@link #completed() done} action would clear the {@link #getDueBy() dueBy} - * property (rather than require the user to have to clear manually). - */ - public String validate() { - if(isComplete() && getDueBy() != null) { - return "Due by date must be set to null if item has been completed"; - } - return null; - } - - - //endregion - - - //region > programmatic helpers - @Programmatic // excluded from the framework's metamodel - public boolean isDue() { - if (getDueBy() == null) { - return false; - } - return !toDoItems.isMoreThanOneWeekInPast(getDueBy()); - } - //endregion - - //region > events - - public static abstract class AbstractActionDomainEvent extends ActionDomainEvent<ToDoItem> { - private static final long serialVersionUID = 1L; - private final String description; - public AbstractActionDomainEvent( - final String description, - final ToDoItem source, - final Identifier identifier, - final Object... arguments) { - super(source, identifier, arguments); - this.description = description; - } - public String getEventDescription() { - return description; - } - } - - public static class CompletedEvent extends AbstractActionDomainEvent { - private static final long serialVersionUID = 1L; - public CompletedEvent( - final ToDoItem source, - final Identifier identifier, - final Object... arguments) { - super("completed", source, identifier, arguments); - } - } - - public static class NoLongerCompletedEvent extends AbstractActionDomainEvent { - private static final long serialVersionUID = 1L; - public NoLongerCompletedEvent( - final ToDoItem source, - final Identifier identifier, - final Object... arguments) { - super("no longer completed", source, identifier, arguments); - } - } - - public static class DeletedEvent extends AbstractActionDomainEvent { - private static final long serialVersionUID = 1L; - public DeletedEvent( - final ToDoItem source, - final Identifier identifier, - final Object... arguments) { - super("deleted", source, identifier, arguments); - } - } - - //endregion - - //region > predicates - - public static class Predicates { - - public static Predicate<ToDoItem> thoseOwnedBy(final String currentUser) { - return new Predicate<ToDoItem>() { - @Override - public boolean apply(final ToDoItem toDoItem) { - return Objects.equal(toDoItem.getOwnedBy(), currentUser); - } - }; - } - - public static Predicate<ToDoItem> thoseCompleted( - final boolean completed) { - return new Predicate<ToDoItem>() { - @Override - public boolean apply(final ToDoItem t) { - return Objects.equal(t.isComplete(), completed); - } - }; - } - - public static Predicate<ToDoItem> thoseWithSimilarDescription(final String description) { - return new Predicate<ToDoItem>() { - @Override - public boolean apply(final ToDoItem t) { - return t.getDescription().contains(description); - } - }; - } - - @SuppressWarnings("unchecked") - public static Predicate<ToDoItem> thoseSimilarTo(final ToDoItem toDoItem) { - return com.google.common.base.Predicates.and( - thoseNot(toDoItem), - thoseOwnedBy(toDoItem.getOwnedBy()), - thoseCategorised(toDoItem.getCategory())); - } - - public static Predicate<ToDoItem> thoseNot(final ToDoItem toDoItem) { - return new Predicate<ToDoItem>() { - @Override - public boolean apply(final ToDoItem t) { - return t != toDoItem; - } - }; - } - - public static Predicate<ToDoItem> thoseCategorised(final Category category) { - return new Predicate<ToDoItem>() { - @Override - public boolean apply(final ToDoItem toDoItem) { - return Objects.equal(toDoItem.getCategory(), category); - } - }; - } - - public static Predicate<ToDoItem> thoseSubcategorised( - final Subcategory subcategory) { - return new Predicate<ToDoItem>() { - @Override - public boolean apply(final ToDoItem t) { - return Objects.equal(t.getSubcategory(), subcategory); - } - }; - } - - public static Predicate<ToDoItem> thoseCategorised( - final Category category, final Subcategory subcategory) { - return com.google.common.base.Predicates.and( - thoseCategorised(category), - thoseSubcategorised(subcategory)); - } - - } - - //endregion - - //region > toString, compareTo - @Override - public String toString() { - return ObjectContracts.toString(this, "description,complete,dueBy,ownedBy"); - } - - /** - * Required so can store in {@link SortedSet sorted set}s (eg {@link #getDependencies()}). - */ - @Override - public int compareTo(final ToDoItem other) { - return ObjectContracts.compare(this, other, "complete,dueBy,description"); - } - //endregion - - //region > injected services - @javax.inject.Inject - DomainObjectContainer container; - - @javax.inject.Inject - ToDoItems toDoItems; - - @javax.inject.Inject - Scratchpad scratchpad; - - /** - * public only so can be injected from integ tests - */ - @javax.inject.Inject - public ActionInvocationContext actionInvocationContext; - - /** - * public only so can be injected from integ tests - */ - @javax.inject.Inject - public EventBusService eventBusService; - - @javax.inject.Inject - WrapperFactory wrapperFactory; - - //endregion - -}
http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.layout.json ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.layout.json b/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.layout.json deleted file mode 100644 index cda5fdc..0000000 --- a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.layout.json +++ /dev/null @@ -1,222 +0,0 @@ -/** - * 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. - */ -{ - "columns": [ - { - "span": 6, - "memberGroups": { - "General": { - "members": { - "description": { - "propertyLayout": { - "cssClass": "x-key", - "labelPosition": "TOP" - } - }, - "category": { - "propertyLayout": { - "labelPosition": "TOP" - } - }, - "subcategory": { - "propertyLayout": { - "labelPosition": "NONE" - }, - "actions": { - "updateCategory": { - "actionLayout": { - "named": "Update" - } - }, - "analyseCategory": { - "actionLayout": { - "cssClass": "btn-default", - "cssClassFa": "fa fa-pie-chart" - } - } - } - }, - "ownedBy": { - "propertyLayout": { - "hidden": "EVERYWHERE" - } - }, - "complete": { - "propertyLayout": { - "named": "Whether this todo item has been <i>completed</i> or not.", - "namedEscaped": false, - "describedAs": "Whether this todo item has been completed, or not.", - "labelPosition": "LEFT" - }, - "actions": { - "completed": { - "actionLayout": { - "named": "Done", - "describedAs": "Update this todo item as complete", - "cssClass": "btn-success", - "cssClassFa": "fa fa-thumbs-up" - } - }, - "notYetCompleted": { - "actionLayout": { - "named": "Not done", - "describedAs": "Update this todo item as not yet complete", - "cssClass": "btn-info", - "cssClassFa": "fa fa-thumbs-down" - } - }, - "scheduleExplicitly": { - }, - "scheduleImplicitly": { - } - } - } - } - }, - "Misc": { - "members": { - "versionSequence": { - "propertyLayout": { - "named": "Version", - "hidden": "ALL_TABLES" - }, - "disabled": {} - } - } - } - } - }, - { - "span": 6, - "memberGroups": { - "Priority": { - "members": { - "relativePriority": { - "actions": { - "previous": { - "actionLayout": { - "cssClassFa": "fa fa-step-backward" - } - }, - "next": { - "actionLayout": { - "cssClassFa": "fa fa-step-forward", - "cssClassFaPosition": "right" - } - } - } - }, - "dueBy": { - "propertyLayout": { - "renderedAsDayBefore": "false" - } - } - } - }, - "Other": { - "members": { - "cost": { - "actions": { - "updateCost":{ - "actionLayout": { - "named": "Update", - "cssClassFa": "fa fa-dollar" - } - } - } - }, - "notes": { - "propertyLayout": { - "hidden": "ALL_TABLES", - "labelPosition": "TOP", - "multiLine": 5 - } - }, - "attachment": { - "propertyLayout": { - "hidden": "STANDALONE_TABLES" - } - }, - "doc": { - "propertyLayout": { - "hidden": "STANDALONE_TABLES" - } - } - } - } - } - }, - { - "span": 0 - }, - { - "span": 6, - "collections": { - "dependencies": { - "collectionLayout": { - "named": "Todo items that are <i>dependencies</i> of this item.", - "namedEscaped": false, - "describedAs": "Todo items that must be completed before this one", - "paged": 5, - "render": "EAGERLY" - }, - "disabled": {}, - "actions": { - "add":{ - "actionLayout": { - "cssClass": "btn-default", - "cssClassFa": "fa fa-plus-square" - } - }, - "remove": { - "actionLayout": { - "cssClass": "x-caution btn-default", - "cssClassFa": "fa fa-minus-square" - } - } - } - }, - "similarTo": { - "collectionLayout": { - "named": "Todo items that are <i>similar to</i> this one", - "namedEscaped": false, - "paged": 3, - "render": "LAZILY" - }, - "disabled": {} - } - } - } - ], - "actions": { - "totalCost": {}, - "delete": { - "actionLayout": { - "cssClass": "btn btn-danger", - "cssClassFa": "fa fa-trash fa-lg" - } - }, - "duplicate": { - "actionLayout": { - "named": "Clone", - "describedAs": "Create a new todo item from this one", - "cssClass": "btn-warn", - "cssClassFa": "fa fa-copy fa-lg" - } - } - } -} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.png ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.png b/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.png deleted file mode 100644 index 99a9fed..0000000 Binary files a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItem.png and /dev/null differ http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java b/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java deleted file mode 100644 index 32b2aa4..0000000 --- a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemChangedPayloadFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 dom.todo; - -import org.apache.isis.applib.annotation.PublishingChangeKind; -import org.apache.isis.applib.annotation.PublishingPayloadFactoryForObject; -import org.apache.isis.applib.services.publish.EventPayload; -import org.apache.isis.applib.services.publish.EventPayloadForObjectChanged; - -public class ToDoItemChangedPayloadFactory implements PublishingPayloadFactoryForObject { - - public static class ToDoItemPayload extends EventPayloadForObjectChanged<ToDoItem> { - - public ToDoItemPayload(ToDoItem changed) { - super(changed); - } - - /** - * Expose the item's {@link ToDoItem#getDescription() description} more explicitly - * in the payload. - */ - public String getDescription() { - return getChanged().getDescription(); - } - } - - @Override - public EventPayload payloadFor(final Object changedObject, final PublishingChangeKind publishingChangeKind) { - return new ToDoItemPayload((ToDoItem) changedObject); - } - -} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemContributions.java ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemContributions.java b/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemContributions.java deleted file mode 100644 index b35135c..0000000 --- a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemContributions.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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 dom.todo; - -import dom.todo.ToDoItem.Category; -import dom.todo.ToDoItem.Subcategory; - -import java.util.List; -import java.util.concurrent.Callable; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Ordering; -import org.joda.time.LocalDate; -import org.apache.isis.applib.AbstractFactoryAndRepository; -import org.apache.isis.applib.annotation.Action; -import org.apache.isis.applib.annotation.ActionLayout; -import org.apache.isis.applib.annotation.Contributed; -import org.apache.isis.applib.annotation.DomainService; -import org.apache.isis.applib.annotation.Editing; -import org.apache.isis.applib.annotation.NatureOfService; -import org.apache.isis.applib.annotation.Optionality; -import org.apache.isis.applib.annotation.Parameter; -import org.apache.isis.applib.annotation.ParameterLayout; -import org.apache.isis.applib.annotation.Property; -import org.apache.isis.applib.annotation.SemanticsOf; -import org.apache.isis.applib.annotation.Where; -import org.apache.isis.applib.query.QueryDefault; -import org.apache.isis.applib.services.queryresultscache.QueryResultsCache; -import org.apache.isis.applib.value.Clob; - -@DomainService(nature = NatureOfService.VIEW_CONTRIBUTIONS_ONLY) -public class ToDoItemContributions extends AbstractFactoryAndRepository { - - //region > priority (contributed property) - @Action( - semantics = SemanticsOf.SAFE, - hidden = Where.ALL_TABLES - ) - @ActionLayout( - describedAs = "The relative priority of this item compared to others not yet complete (using 'due by' date)", - contributed = Contributed.AS_ASSOCIATION - ) - @Property( - editing = Editing.DISABLED, - editingDisabledReason = "Relative priority, derived from due date" - ) - public Integer relativePriority(final ToDoItem toDoItem) { - return queryResultsCache.execute(new Callable<Integer>(){ - @Override - public Integer call() throws Exception { - if(toDoItem.isComplete()) { - return null; - } - - // sort items, then locate this one - int i=1; - for (final ToDoItem each : sortedNotYetComplete()) { - if(each == toDoItem) { - return i; - } - i++; - } - return null; - }}, ToDoItemContributions.class, "relativePriority", toDoItem); - } - - private List<ToDoItem> sortedNotYetComplete() { - return ORDERING_DUE_BY - .compound(ORDERING_DESCRIPTION) - .sortedCopy(toDoItems.notYetComplete()); - } - - private static final Ordering<ToDoItem> ORDERING_DUE_BY = - Ordering.natural().nullsLast().onResultOf(new Function<ToDoItem, LocalDate>(){ - @Override - public LocalDate apply(final ToDoItem input) { - return input.getDueBy(); - } - }); - - private static final Ordering<ToDoItem> ORDERING_DESCRIPTION = - Ordering.natural().nullsLast().onResultOf(new Function<ToDoItem, String>(){ - @Override - public String apply(final ToDoItem input) { - return input.getDescription(); - } - }); - - - //endregion - - //region > next, previous (contributed actions) - @Action(semantics = SemanticsOf.SAFE) - @ActionLayout( - describedAs = "The next item not yet completed", - contributed = Contributed.AS_ACTION - ) - public ToDoItem next(final ToDoItem item) { - final Integer priority = relativePriority(item); - if(priority == null) { - return item; - } - int priorityOfNext = priority != null ? priority + 1 : 0; - return itemWithPriorityElse(priorityOfNext, item); - } - public String disableNext(final ToDoItem toDoItem) { - if (toDoItem.isComplete()) { - return "Completed"; - } - if(next(toDoItem) == null) { - return "No next item"; - } - return null; - } - - // ////////////////////////////////////// - - @ActionLayout( - describedAs = "The previous item not yet completed", - contributed = Contributed.AS_ACTION - ) - @Action(semantics = SemanticsOf.SAFE) - public ToDoItem previous(final ToDoItem item) { - final Integer priority = relativePriority(item); - if(priority == null) { - return item; - } - int priorityOfPrevious = priority != null? priority - 1 : 0; - return itemWithPriorityElse(priorityOfPrevious, item); - } - public String disablePrevious(final ToDoItem toDoItem) { - if (toDoItem.isComplete()) { - return "Completed"; - } - if(previous(toDoItem) == null) { - return "No previous item"; - } - return null; - } - - // ////////////////////////////////////// - - /** - * @param priority : 1-based priority - */ - private ToDoItem itemWithPriorityElse(int priority, final ToDoItem itemElse) { - if(priority < 1) { - return null; - } - final List<ToDoItem> items = sortedNotYetComplete(); - if(priority > items.size()) { - return null; - } - return priority>=0 && items.size()>=priority? items.get(priority-1): itemElse; - } - //endregion - - //region > similarTo (contributed collection) - @ActionLayout( - contributed = Contributed.AS_ASSOCIATION - ) - @Action(semantics = SemanticsOf.SAFE) - public List<ToDoItem> similarTo(final ToDoItem toDoItem) { - final List<ToDoItem> similarToDoItems = allMatches( - new QueryDefault<ToDoItem>(ToDoItem.class, - "findByOwnedByAndCategory", - "ownedBy", currentUserName(), - "category", toDoItem.getCategory())); - return Lists.newArrayList(Iterables.filter(similarToDoItems, excluding(toDoItem))); - } - - - private static Predicate<ToDoItem> excluding(final ToDoItem toDoItem) { - return new Predicate<ToDoItem>() { - @Override - public boolean apply(ToDoItem input) { - return input != toDoItem; - } - }; - } - //endregion - - //region > updateCategory (contributed action) - - @ActionLayout( - describedAs = "Update category and subcategory" - ) - @Action(semantics = SemanticsOf.IDEMPOTENT) - public Categorized updateCategory( - final Categorized item, - final @ParameterLayout(named="Category") Category category, - final @Parameter(optionality = Optionality.OPTIONAL) @ParameterLayout(named="Subcategory") Subcategory subcategory) { - item.setCategory(category); - item.setSubcategory(subcategory); - return item; - } - public Category default1UpdateCategory( - final Categorized item) { - return item != null? item.getCategory(): null; - } - public Subcategory default2UpdateCategory( - final Categorized item) { - return item != null? item.getSubcategory(): null; - } - - public List<Subcategory> choices2UpdateCategory( - final Categorized item, final Category category) { - return Subcategory.listFor(category); - } - - public String validateUpdateCategory( - final Categorized item, final Category category, final Subcategory subcategory) { - return Subcategory.validate(category, subcategory); - } - //endregion - - // region > exportAsJson (action) - /** - * Demonstrates functionality of streaming back Clob/Blob result within an action with a prompt, i.e. Ajax request - */ - @Action(semantics = SemanticsOf.SAFE) - public Clob exportAsJson( - final ToDoItem toDoItem, - @ParameterLayout(named = "File name") String fileName - ) { - if(!fileName.endsWith(".json")) { - fileName += ".json"; - } - return new Clob( - fileName, - "application/json", - "{" + - "\"description\": \"" + toDoItem.getDescription()+"\"" + - ",\"complete\": " + ""+toDoItem.isComplete() + - "}"); - } - - public String default1ExportAsJson() { - return "todo"; - } - //endregion - - - //region > helpers - protected String currentUserName() { - return getContainer().getUser().getName(); - } - //endregion - - //region > injected services - @javax.inject.Inject - private ToDoItems toDoItems; - - @javax.inject.Inject - private QueryResultsCache queryResultsCache; - //endregion - -} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java b/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java deleted file mode 100644 index 91b0ee8..0000000 --- a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItemSubscriptions.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * 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 dom.todo; - -import java.util.EventObject; -import java.util.List; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Lists; -import com.google.common.eventbus.Subscribe; -import org.apache.isis.applib.DomainObjectContainer; -import org.apache.isis.applib.NonRecoverableException; -import org.apache.isis.applib.RecoverableException; -import org.apache.isis.applib.annotation.Action; -import org.apache.isis.applib.annotation.ActionLayout; -import org.apache.isis.applib.annotation.DomainService; -import org.apache.isis.applib.annotation.DomainServiceLayout; -import org.apache.isis.applib.annotation.MemberOrder; -import org.apache.isis.applib.annotation.NatureOfService; -import org.apache.isis.applib.annotation.ParameterLayout; -import org.apache.isis.applib.annotation.Programmatic; -import org.apache.isis.applib.annotation.RestrictTo; -import org.apache.isis.applib.annotation.SemanticsOf; -import org.apache.isis.applib.services.eventbus.ActionDomainEvent; -import org.apache.isis.applib.services.eventbus.CollectionDomainEvent; -import org.apache.isis.applib.services.eventbus.EventBusService; -import org.apache.isis.applib.services.eventbus.PropertyDomainEvent; - -import static com.google.common.collect.Iterables.filter; -import static com.google.common.collect.Iterables.transform; -import static com.google.common.collect.Lists.newArrayList; - -/** - * Subscribes to changes made to the {@link dom.todo.ToDoItem} entity. - * - * <p> - * (For demo purposes) the behaviour can be influenced using {@link #subscriberBehaviour(dom.todo.ToDoItemSubscriptions.Behaviour)}. - * In particular, the subscriber can be used to hide/disable/validate actions, or just to perform pre- or post-execute - * tasks. This also includes being set to throw an exception during the execution of the action (also in effect - * vetoing the change). - * </p> - */ -@DomainService(nature = NatureOfService.VIEW_MENU_ONLY) -@DomainServiceLayout(menuBar = DomainServiceLayout.MenuBar.SECONDARY, menuOrder = "30") -public class ToDoItemSubscriptions { - - //region > LOG - private final static org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(ToDoItemSubscriptions.class); - //endregion - - //region > postConstruct, preDestroy - - /** - * Registers this service with the {@link org.apache.isis.applib.services.eventbus.EventBusService}. - * - * <p> - * Because this service is a singleton, this is called during initial bootstrap. - * </p> - */ - @Programmatic - @PostConstruct - public void postConstruct() { - LOG.info("postConstruct: registering to event bus"); - eventBusService.register(this); - } - - /** - * Unregisters this service from the {@link org.apache.isis.applib.services.eventbus.EventBusService}. - * - * <p> - * Because this service is a singleton, this is only done when the system is shutdown. - * </p> - */ - @Programmatic - @PreDestroy - public void preDestroy() { - LOG.info("preDestroy: unregistering from event bus"); - eventBusService.unregister(this); - } - //endregion - - - //region > on(Event)... - - public static enum Behaviour { - AnyExecuteAccept, - AnyExecuteVetoWithRecoverableException, - AnyExecuteVetoWithNonRecoverableException, - AnyExecuteVetoWithOtherException, - UpdateCostActionHide, - UpdateCostActionDisable, - UpdateCostActionInvalidate, - DescriptionPropertyHide, - DescriptionPropertyDisable, - DescriptionPropertyInvalidate, - DependenciesCollectionHide, - // not implemented in Wicket viewer, but supported in wrapped objects - DependenciesCollectionDisable, - // not implemented in Wicket viewer, but supported in wrapped objects - DependenciesCollectionInvalidateAdd, - DependenciesCollectionInvalidateRemove, - SimilarToCollectionHide - } - - /** - * The desired behaviour of this service. - */ - private Behaviour behaviour = Behaviour.AnyExecuteAccept; - - /** - * To demo/test what occurs if a subscriber that might veto an event. - */ - @MemberOrder(name = "Prototyping", sequence = "80") - @ActionLayout( - named="Set subscriber behaviour" - ) - @Action( - semantics = SemanticsOf.IDEMPOTENT, - restrictTo = RestrictTo.PROTOTYPING - ) - public void subscriberBehaviour( - @ParameterLayout( - named="Behaviour" - ) - final Behaviour behaviour) { - this.behaviour = behaviour; - container.informUser("Subscriber behaviour set to: " + behaviour); - } - public Behaviour default0SubscriberBehaviour() { - return this.behaviour; - } - - @Programmatic - public Behaviour getSubscriberBehaviour() { - return behaviour; - } - - - private void onExecutedThrowExceptionIfSet(final ActionDomainEvent<?> ev) { - if(ev != null && ev.getSemantics().isSafe()) { - return; - } - onExecutedThrowExceptionIfSet(); - } - private void onExecutedThrowExceptionIfSet(final PropertyDomainEvent<?, ?> ev) { - onExecutedThrowExceptionIfSet(); - } - private void onExecutedThrowExceptionIfSet(final CollectionDomainEvent<?, ?> ev) { - onExecutedThrowExceptionIfSet(); - } - - - private void onExecutedThrowExceptionIfSet() { - if(behaviour == Behaviour.AnyExecuteVetoWithRecoverableException) { - throw new RecoverableException("Rejecting event (recoverable exception thrown)"); - } - if(behaviour == Behaviour.AnyExecuteVetoWithNonRecoverableException) { - throw new NonRecoverableException("Rejecting event (non-recoverable exception thrown)"); - } - if(behaviour == Behaviour.AnyExecuteVetoWithOtherException) { - throw new RuntimeException("Throwing some other exception"); - } - } - //endregion - - //region > on(Event) for ToDoItem-specific events - @Programmatic - @Subscribe - public void on(final ToDoItem.CompletedEvent ev) { - recordEvent(ev); - switch(ev.getEventPhase()) { - case HIDE: - break; - case DISABLE: - break; - case VALIDATE: - break; - case EXECUTING: - break; - case EXECUTED: - LOG.info("Received ToDoItem.CompletedEvent for : " + ev.getSource().toString()); - break; - } - } - //endregion - - //region > on(Event) ... general purpose - - @Programmatic - @Subscribe - public void on(final ActionDomainEvent<?> ev) { - recordEvent(ev); - switch(ev.getEventPhase()) { - case HIDE: - if(getSubscriberBehaviour() == Behaviour.UpdateCostActionHide) { - if(ev.getIdentifier().getMemberName().equals("updateCost")) { - ev.hide(); - } - } - break; - case DISABLE: - if(getSubscriberBehaviour() == Behaviour.UpdateCostActionDisable) { - if(ev.getIdentifier().getMemberName().equals("updateCost")) { - ev.disable("ToDoItemSubscriptions says: updateCost action disabled!"); - } - } - break; - case VALIDATE: - if(getSubscriberBehaviour() == Behaviour.UpdateCostActionInvalidate && - ev.getIdentifier().getMemberName().equals("updateCost")) { - ev.invalidate("ToDoItemSubscriptions says: can't invoke updateCost action with these args!"); - } - break; - case EXECUTING: - break; - case EXECUTED: - LOG.info("Received ActionDomainEvent, " + ev.getSource().toString() + ", invoked " + ev.getIdentifier().getMemberName()); - onExecutedThrowExceptionIfSet(ev); - break; - } - } - - @Programmatic - @Subscribe - public void on(PropertyDomainEvent<?,?> ev) { - recordEvent(ev); - switch(ev.getEventPhase()) { - case HIDE: - if(getSubscriberBehaviour() == Behaviour.DescriptionPropertyHide && - ev.getIdentifier().getMemberName().equals("description")) { - ev.veto(""); - } - break; - case DISABLE: - if(getSubscriberBehaviour() == Behaviour.DescriptionPropertyDisable && - ev.getIdentifier().getMemberName().equals("description")) { - ev.veto("ToDoItemSubscriptions says: description property disabled!"); - } - break; - case VALIDATE: - if(getSubscriberBehaviour() == Behaviour.DescriptionPropertyInvalidate && - ev.getIdentifier().getMemberName().equals("description")) { - ev.veto("ToDoItemSubscriptions says: can't change description property to this value!"); - } - break; - case EXECUTING: - break; - case EXECUTED: - LOG.info("Received PropertyDomainEvent, " + ev.getSource().toString() + ", changed " + ev.getIdentifier().getMemberName() + " : " + ev.getOldValue() + " -> " + ev.getNewValue()); - onExecutedThrowExceptionIfSet(ev); - - if(ev.getIdentifier().getMemberName().contains("description")) { - String newValue = (String) ev.getNewValue(); - if(newValue.matches(".*demo veto.*")) { - throw new RecoverableException("oh no you don't! " + ev.getNewValue()); - } - } - break; - } - } - - @Programmatic - @Subscribe - public void on(CollectionDomainEvent<?,?> ev) { - recordEvent(ev); - switch (ev.getEventPhase()) { - case HIDE: - if(getSubscriberBehaviour() == Behaviour.DependenciesCollectionHide && - ev.getIdentifier().getMemberName().equals("dependencies")) { - ev.veto(""); - } - if (getSubscriberBehaviour() == Behaviour.SimilarToCollectionHide && - ev.getIdentifier().getMemberName().equals("similarTo")) { - ev.veto(""); - } - break; - case DISABLE: - if (getSubscriberBehaviour() == Behaviour.DependenciesCollectionDisable && - ev.getIdentifier().getMemberName().equals("dependencies")) { - ev.veto("ToDoItemSubscriptions says: dependencies collection disabled!"); - } - break; - case VALIDATE: - if(getSubscriberBehaviour() == Behaviour.DependenciesCollectionInvalidateAdd && - ev.getIdentifier().getMemberName().equals("dependencies") && - ev.getOf() == CollectionDomainEvent.Of.ADD_TO ) { - ev.veto("ToDoItemSubscriptions says: can't add this object to dependencies collection!"); - } - if(getSubscriberBehaviour() == Behaviour.DependenciesCollectionInvalidateRemove && - ev.getIdentifier().getMemberName().equals("dependencies") && - ev.getOf() == CollectionDomainEvent.Of.REMOVE_FROM ) { - ev.veto("ToDoItemSubscriptions says: can't remove this object from dependencies collection!"); - } - break; - case EXECUTING: - break; - case EXECUTED: - if(ev.getOf() == CollectionDomainEvent.Of.ADD_TO) { - LOG.info("Received CollectionDomainEvent, " + ev.getSource().toString() + ", added to " + ev.getIdentifier().getMemberName() + " : " + ev.getValue()); - } else { - LOG.info("Received CollectionDomainEvent, " + ev.getSource().toString() + ", removed from " + ev.getIdentifier().getMemberName() + " : " + ev.getValue()); - } - onExecutedThrowExceptionIfSet(ev); - break; - } - - } - - //endregion - - //region > receivedEvents - private final List<java.util.EventObject> receivedEvents = Lists.newLinkedList(); - - /** - * Used in integration tests. - */ - @Programmatic - public List<java.util.EventObject> receivedEvents() { - return receivedEvents; - } - - /** - * Used in integration tests. - */ - @Programmatic - public <T extends java.util.EventObject> List<T> receivedEvents(final Class<T> expectedType) { - return newArrayList( - transform( - filter(receivedEvents, instanceOf(expectedType)), - castTo(expectedType))); - } - - private static <T extends EventObject> Function<EventObject, T> castTo(Class<T> expectedType) { - return new Function<EventObject, T>() { - @Override - public T apply(EventObject input) { - return (T) input; - } - }; - } - - private static <T extends EventObject> Predicate<EventObject> instanceOf(final Class<T> expectedType) { - return new Predicate<EventObject>() { - @Override - public boolean apply(EventObject input) { - return expectedType.isInstance(input); - } - }; - } - - /** - * Used in integration tests. - */ - @Programmatic - public <T extends java.util.EventObject> T mostRecentlyReceivedEvent(Class<T> expectedType) { - final List<T> receivedEvents = receivedEvents(expectedType); - return !receivedEvents.isEmpty() ? receivedEvents.get(0) : null; - } - private void recordEvent(final java.util.EventObject ev) { - receivedEvents.add(0, ev); - } - /** - * Used in integration tests. - */ - @Programmatic - public void reset() { - receivedEvents.clear(); - subscriberBehaviour(ToDoItemSubscriptions.Behaviour.AnyExecuteAccept); - } - //endregion - - //region > injected services - @javax.inject.Inject - private DomainObjectContainer container; - - @javax.inject.Inject - private EventBusService eventBusService; - //endregion - - -} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItems.java ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItems.java b/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItems.java deleted file mode 100644 index da392a5..0000000 --- a/example/application/todoapp/dom/src/main/java/dom/todo/ToDoItems.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 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 dom.todo; - -import dom.todo.ToDoItem.Category; -import dom.todo.ToDoItem.Subcategory; - -import java.math.BigDecimal; -import java.util.List; -import com.google.common.base.Predicates; -import org.joda.time.LocalDate; -import org.apache.isis.applib.DomainObjectContainer; -import org.apache.isis.applib.annotation.Action; -import org.apache.isis.applib.annotation.ActionLayout; -import org.apache.isis.applib.annotation.BookmarkPolicy; -import org.apache.isis.applib.annotation.DomainService; -import org.apache.isis.applib.annotation.DomainServiceLayout; -import org.apache.isis.applib.annotation.RestrictTo; -import org.apache.isis.applib.annotation.MemberOrder; -import org.apache.isis.applib.annotation.Optionality; -import org.apache.isis.applib.annotation.Parameter; -import org.apache.isis.applib.annotation.ParameterLayout; -import org.apache.isis.applib.annotation.Programmatic; -import org.apache.isis.applib.annotation.SemanticsOf; -import org.apache.isis.applib.query.QueryDefault; -import org.apache.isis.applib.services.clock.ClockService; - -@DomainServiceLayout(named="ToDos", menuOrder = "10") -@DomainService(repositoryFor = ToDoItem.class) -public class ToDoItems { - - //region > notYetComplete (action) - @Action(semantics = SemanticsOf.SAFE) - @ActionLayout( - cssClassFa = "fa fa-thumbs-down", - bookmarking = BookmarkPolicy.AS_ROOT - ) - @MemberOrder(sequence = "10") - public List<ToDoItem> notYetComplete() { - final List<ToDoItem> items = notYetCompleteNoUi(); - if(items.isEmpty()) { - container.informUser("All to-do items have been completed :-)"); - } - return items; - } - - @Programmatic - public List<ToDoItem> notYetCompleteNoUi() { - return container.allMatches( - new QueryDefault<>(ToDoItem.class, - "findByOwnedByAndCompleteIsFalse", - "ownedBy", currentUserName())); - } - //endregion - - //region > complete (action) - @ActionLayout( - cssClassFa = "fa fa-thumbs-up" - ) - @Action(semantics = SemanticsOf.SAFE) - @MemberOrder(sequence = "20") - public List<ToDoItem> complete() { - final List<ToDoItem> items = completeNoUi(); - if(items.isEmpty()) { - container.informUser("No to-do items have yet been completed :-("); - } - return items; - } - - @Programmatic - public List<ToDoItem> completeNoUi() { - return container.allMatches( - new QueryDefault<>(ToDoItem.class, - "findByOwnedByAndCompleteIsTrue", - "ownedBy", currentUserName())); - } - //endregion - - //region > categorized (action) - @SuppressWarnings("unchecked") - @Action(semantics = SemanticsOf.SAFE) - @ActionLayout( - cssClassFa = "fa fa-question", - bookmarking = BookmarkPolicy.AS_ROOT - ) - @MemberOrder(sequence = "40") - public List<ToDoItem> categorized( - @ParameterLayout(named="Category") final Category category, - @ParameterLayout(named="Subcategory") final Subcategory subcategory, - @ParameterLayout(named="Completed?") final boolean completed) { - // an example "naive" implementation (filtered in Java code, not DBMS) - return container.allMatches(ToDoItem.class, - Predicates.and( - ToDoItem.Predicates.thoseOwnedBy(currentUserName()), - ToDoItem.Predicates.thoseCompleted(completed), - ToDoItem.Predicates.thoseCategorised(category, subcategory))); - } - public Category default0Categorized() { - return Category.Professional; - } - public Subcategory default1Categorized() { - return default0Categorized().subcategories().get(0); - } - public boolean default2Categorized() { - return false; - } - public List<Subcategory> choices1Categorized( - final Category category) { - return Subcategory.listFor(category); - } - public String validateCategorized( - final Category category, - final Subcategory subcategory, - final boolean completed) { - return Subcategory.validate(category, subcategory); - } - //endregion - - //region > newToDo (action) - @ActionLayout(cssClassFa = "fa fa-plus") - @MemberOrder(sequence = "5") - public ToDoItem newToDo( - @Parameter(regexPattern = "\\w[@&:\\-\\,\\.\\+ \\w]*") - @ParameterLayout(named="Description") - final String description, - @ParameterLayout(named="Category") - final Category category, - @Parameter(optionality = Optionality.OPTIONAL) - @ParameterLayout(named="Subcategory") - final Subcategory subcategory, - @Parameter(optionality = Optionality.OPTIONAL) - @ParameterLayout(named="Due by") - final LocalDate dueBy, - @Parameter(optionality = Optionality.OPTIONAL) - @ParameterLayout(named="Cost") - final BigDecimal cost) { - return newToDo(description, category, subcategory, currentUserName(), dueBy, cost); - } - public Category default1NewToDo() { - return Category.Professional; - } - public Subcategory default2NewToDo() { - return Category.Professional.subcategories().get(0); - } - public LocalDate default3NewToDo() { - return clockService.now().plusDays(14); - } - public List<Subcategory> choices2NewToDo( - final String description, final Category category) { - return Subcategory.listFor(category); - } - public String validateNewToDo( - final String description, - final Category category, final Subcategory subcategory, - final LocalDate dueBy, final BigDecimal cost) { - return Subcategory.validate(category, subcategory); - } - //endregion - - //region > allToDos (action) - @ActionLayout( - cssClassFa = "fa fa-globe" - ) - @Action( - semantics = SemanticsOf.SAFE, - restrictTo = RestrictTo.PROTOTYPING - ) - @MemberOrder(sequence = "50") - public List<ToDoItem> allToDos() { - final List<ToDoItem> items = container.allMatches( - new QueryDefault<>(ToDoItem.class, - "findByOwnedBy", - "ownedBy", currentUserName())); - if(items.isEmpty()) { - container.warnUser("No to-do items found."); - } - return items; - } - //endregion - - //region > autoComplete (programmatic) - @Programmatic // not part of metamodel - public List<ToDoItem> autoComplete(final String description) { - return container.allMatches( - new QueryDefault<>(ToDoItem.class, - "findByOwnedByAndDescriptionContains", - "ownedBy", currentUserName(), - "description", description)); - } - //endregion - - //region > helpers - @Programmatic // for use by fixtures - public ToDoItem newToDo( - final String description, - final Category category, - final Subcategory subcategory, - final String userName, - final LocalDate dueBy, final BigDecimal cost) { - final ToDoItem toDoItem = container.newTransientInstance(ToDoItem.class); - toDoItem.setDescription(description); - toDoItem.setCategory(category); - toDoItem.setSubcategory(subcategory); - toDoItem.setOwnedBy(userName); - toDoItem.setDueBy(dueBy); - toDoItem.setCost(cost); - - container.persist(toDoItem); - container.flush(); - - return toDoItem; - } - - private String currentUserName() { - return container.getUser().getName(); - } - - //endregion - - //region > common validation - private static final long ONE_WEEK_IN_MILLIS = 7 * 24 * 60 * 60 * 1000L; - - @Programmatic - public String validateDueBy(final LocalDate dueBy) { - return isMoreThanOneWeekInPast(dueBy) ? "Due by date cannot be more than one week old" : null; - } - @Programmatic - boolean isMoreThanOneWeekInPast(final LocalDate dueBy) { - return dueBy.toDateTimeAtStartOfDay().getMillis() < clockService.nowAsMillis() - ONE_WEEK_IN_MILLIS; - } - //endregion - - //region > injected services - @javax.inject.Inject - private DomainObjectContainer container; - - @javax.inject.Inject - private ClockService clockService; - //endregion - -} http://git-wip-us.apache.org/repos/asf/isis/blob/a4ec0b72/example/application/todoapp/dom/src/test/java/dom/todo/ToDoItemTest.java ---------------------------------------------------------------------- diff --git a/example/application/todoapp/dom/src/test/java/dom/todo/ToDoItemTest.java b/example/application/todoapp/dom/src/test/java/dom/todo/ToDoItemTest.java deleted file mode 100644 index b1f5168..0000000 --- a/example/application/todoapp/dom/src/test/java/dom/todo/ToDoItemTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * 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 dom.todo; - -import org.jmock.Expectations; -import org.jmock.auto.Mock; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.apache.isis.applib.DomainObjectContainer; -import org.apache.isis.applib.services.actinvoc.ActionInvocationContext; -import org.apache.isis.applib.security.RoleMemento; -import org.apache.isis.applib.security.UserMemento; -import org.apache.isis.applib.services.eventbus.EventBusService; -import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2; -import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; - -public abstract class ToDoItemTest { - - @Rule - public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES); - - @Mock - EventBusService eventBusService; - - ToDoItem toDoItem; - - @Before - public void setUp() throws Exception { - toDoItem = new ToDoItem(); - - toDoItem.actionInvocationContext = ActionInvocationContext.onObject(toDoItem); - toDoItem.eventBusService = eventBusService; - - context.ignoring(eventBusService); - } - - public static class Properties extends ToDoItemTest { - - @Mock - DomainObjectContainer mockContainer; - - public static class DueBy extends Properties { - - @Test - public void hiddenForNoDueByRole() { - final UserMemento userWithRole = new UserMemento("user", new RoleMemento("realm1:noDueBy_role")); - context.checking(new Expectations() {{ - allowing(mockContainer).getUser(); - will(returnValue(userWithRole)); - }}); - - toDoItem.container = mockContainer; - - assertThat(toDoItem.hideDueBy(), is(true)); - } - - @Test - public void notHiddenWithoutRole() { - final UserMemento userWithRole = new UserMemento("user", new RoleMemento("realm1:someOtherRole")); - context.checking(new Expectations() {{ - allowing(mockContainer).getUser(); - will(returnValue(userWithRole)); - }}); - - toDoItem.container = mockContainer; - - assertThat(toDoItem.hideDueBy(), is(false)); - } - } - - } - - public static class Actions extends ToDoItemTest { - - public static class Completed extends Actions { - - @Test - public void happyCase() throws Exception { - - // given - toDoItem.setComplete(false); - assertThat(toDoItem.disableCompleted(), is(nullValue())); - - // when - toDoItem.completed(); - - // then - assertThat(toDoItem.isComplete(), is(true)); - assertThat(toDoItem.disableCompleted(), is(not(nullValue()))); - } - } - - public static class NotYetCompleted extends Actions { - - @Test - public void happyCase() throws Exception { - - // given - toDoItem.setComplete(true); - assertThat(toDoItem.disableNotYetCompleted(), is(nullValue())); - - // when - toDoItem.notYetCompleted(); - - // then - assertThat(toDoItem.isComplete(), is(false)); - assertThat(toDoItem.disableNotYetCompleted(), is(not(nullValue()))); - } - } - } - - -}
