http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_domain-services_scoped-services.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_domain-services_scoped-services.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_domain-services_scoped-services.adoc new file mode 100644 index 0000000..61c48ff --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_domain-services_scoped-services.adoc @@ -0,0 +1,23 @@ +[[_ugfun_programming-model_domain-services_scoped-services]] += Scoped services +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + +By default all domain services are considered to be singletons, and thread-safe. + +Sometimes though a service's lifetime is applicable only to a single request; in other words it is request-scoped. + +The CDI annotation xref:../rgant/rgant.adoc#_rgant-RequestScoped[`@javax.enterprise.context.RequestScoped`] is used to indicate this fact: + +[source,java] +---- +@javax.enterprise.context.RequestScoped +public class MyService extends AbstractService { + ... +} +---- + +The framework provides a number of request-scoped services, include a xref:../rgsvc/rgsvc.adoc#_rgsvc_api_Scratchpad[`Scratchpad`] service query results caching through the xref:../rgsvc/rgsvc.adoc#_rgsvc_api_QueryResultsCache[`QueryResultsCache`], and support for co-ordinating bulk actions through the xref:../rgsvc/rgsvc.adoc#_rgsvc_api_ActionInvocationContext[`ActionInvocationContext`] service. See the xref:../rgsvc/rgsvc.adoc#[domain services] reference guide for further details. +
http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins.adoc index 1a0a9a3..d6c3f45 100644 --- a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins.adoc +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins.adoc @@ -6,12 +6,11 @@ :_imagesdir: images/ - +A xref:../ugfun/ugfun.adoc#_ugfun_building-blocks_types-of-domain-objects_mixins[mixin] acts like a trait or extension method, allowing one module to contribute behaviour or derived state to another object. Syntactically, a mixin is defined using either the xref:../rgant/rgant.adoc#_rgant_Mixin[`@Mixin`] annotation or using xref:../rgant/rgant.adoc#_rgant_DomainObject_nature[`@DomainObject#nature()`] attribute (specifying a nature of `Nature.MIXIN`). - [source,java] ---- @Mixin(method="coll") // <1> @@ -41,306 +40,18 @@ This could also be an interface. This requires that the action has safe semantics, ie does not alter state/no side-effects. +include::_ugfun_programming-model_mixins_contributed-collection.adoc[leveloffset=+1] +include::_ugfun_programming-model_mixins_contributed-property.adoc[leveloffset=+1] +include::_ugfun_programming-model_mixins_contributed-action.adoc[leveloffset=+1] +include::_ugfun_programming-model_mixins_inferred-name.adoc[leveloffset=+1] +include::_ugfun_programming-model_mixins_nested-static-classes .adoc[leveloffset=+1] -'''' -'''' - -== Contributed services - -Contributed services provide many of the same benefits as xref:../ugbtb/ugbtb.adoc#_ugbtb_decoupling_mixins[mixins]; -indeed mixins are an evolution and refinement of the contributions concept. - -[WARNING] -==== -It's possible that contributions may be deprecated and eventually removed in a future version of the framework, to be replaced entirely by mixins. -==== - -The main difference between contributed services and mixins is that the actions of a contributed service will -contribute to _all_ the parameters of its actions, whereas a mixin only contributes to the type accepted in its -constructor. Also, contributed services are long-lived -singletons, whereas mixins are instantiated as required (by the framework) and then discarded almost immediately. - -[NOTE] -==== -There's further useful information on contributed services in the reference guide, discussing the xref:../rgant/rgant.adoc#_rgant-DomainService_nature[@DomainService#nature()] attribute, for the `NatureOfService.VIEW_CONTRIBUTIONS_ONLY` nature. -==== - - -=== Syntax - -Any n-parameter action provided by a service will automatically be contributed to the list of actions for each of its (entity) parameters. From the viewpoint of the entity the action is called a contributed action. - -For example, given a service: - -[source,java] ----- -public interface Library { - public Loan borrow(Loanable l, Borrower b); -} ----- - -and the entities: - -[source,java] ----- -public class Book implements Loanable { ... } ----- - -and - -[source,java] ----- -public class LibraryMember implements Borrower { ... } ----- - -then the `borrow(...)` action will be contributed to both `Book` and to `LibraryMember`. - -This is an important capability because it helps to decouple the concrete classes from the services. - -If necessary, though, this behaviour can be suppressed by annotating the service action with `@org.apache.isis.applib.annotations.NotContributed`. - -For example: - -[source,java] ----- -public interface Library { - @NotContributed - public Loan borrow(Loanable l, Borrower b); -} ----- +include::_ugfun_programming-model_mixins_programmatic-usage .adoc[leveloffset=+1] -If annotated at the interface level, then the annotation will be inherited by every concrete class. Alternatively the annotation can be applied at the implementation class level and only apply to that particular implementation. - -Note that an action annotated as being `@NotContributed` will still appear in the service menu for the service. If an action should neither be contributed nor appear in service menu items, then simply annotate it as `@Hidden`. - - -## Contributed Action - -NOTE: FIXME - -## Contributed Property - -NOTE: FIXME - -## Contributed Collection - -NOTE: FIXME - - - - - -== Contributed Collection - -The example below shows how to contribute a collection: - -[source,java] ----- -@Mixin -public class DocumentHolderDocuments { - - private final DocumentHolder holder; - public DocumentHolderDocuments(DocumentHolder holder) { this.holder = holder; } - - @Action(semantics=SemanticsOf.SAFE) // <1> - @ActionLayout(contributed = Contributed.AS_ASSOCIATION) // <2> - @CollectionLayout(render = RenderType.EAGERLY) - public List<Document> documents() { // <3> - ... - } - public boolean hideDocuments() { ... } // <4> -} ----- -<1> required; actions that have side-effects cannot be contributed as collections -<2> required; otherwise the mixin will default to being rendered as an action -<3> must accept no arguments. - The mixin is a collection rather than a property because the return type is a collection, not a scalar. -<4> supporting methods follow the usual naming conventions. - (That said, in the case of collections, because the collection is derived/read-only, the only supporting method that is relevant is `hideXxx()`). - -The above will result in a contributed collection for all types that implement/extend from `DocumentHolder` (so is probably for a mixin across modules). - - - -== Contributed Property - -Contributed properties are defined similarly, for example: - -[source,java] ----- -@Mixin -public class DocumentHolderMostRecentDocument { - - private final DocumentHolder holder; - public DocumentHolderDocuments(DocumentHolder holder) { this.holder = holder; } - - @Action(semantics=SemanticsOf.SAFE) // <1> - @ActionLayout(contributed = Contributed.AS_ASSOCIATION) // <2> - public Document> mostRecentDocument() { // <3> - ... - } - public boolean hideMostRecentDocument() { ... } // <4> -} ----- -<1> required; actions that have side-effects cannot be contributed as collections -<2> required; otherwise the mixin will default to being rendered as an action -<3> must accept no arguments. - The mixin is a property rather than a collection because the return type is a scalar. -<4> supporting methods follow the usual naming conventions. - (That said, in the case of properties, because the property is derived/read-only, the only supporting method that is relevant is `hideXxx()`). - - -== Contributed Action - -Contributed properties are defined similarly, for example: - -[source,java] ----- -@Mixin -public class DocumentHolderAddDocument { - - private final DocumentHolder holder; - public DocumentHolderDocuments(DocumentHolder holder) { this.holder = holder; } - - @Action() - @ActionLayout(contributed = Contributed.AS_ACTION) // <1> - public Document> addDocument(Document doc) { - ... - } - public boolean hideAddDocument() { ... } // <2> -} ----- -<1> recommended -<2> supporting methods follow the usual naming conventions. - - -== Inferred Name - -Where the mixin follows the naming convention `SomeType_mixinName` then the method name can be abbreviated to "$$". -The mixin name is everything after the last '_'. - -For example: - -[source,java] ----- -@Mixin -public class DocumentHolder_documents { - - private final DocumentHolder holder; - public DocumentHolder_documents(DocumentHolder holder) { this.holder = holder; } - - @Action(semantics=SemanticsOf.SAFE) - @ActionLayout(contributed = Contributed.AS_ASSOCIATION) - @CollectionLayout(render = RenderType.EAGERLY) - public List<Document> $$() { // <1> - ... - } - public boolean hide$$() { ... } // <2> -} ----- -<1> using "$$" as the reserved method name -<2> supporting methods as usual - -The character "$" is also recognized as a separator between the mixin type and mixin name. -This is useful for mixins implemented as nested static types, discussed below. - - -== As Nested Static Classes - -As noted in the introduction, while mixins were originally introduced as a means of allowing contributions from one module to the types of another module, they are also a convenient mechanism for grouping functionality/behaviour against a concrete type. -All the methods and supporting methods end up in a single construct, and the dependency between that functionality and the rest of the object is made more explicit. - -When using mixins in this fashion, it is idiomatic to write the mixin as a nested static class, using the naming convention described above to reduce duplication. - -For example: - -[source,java] ----- -public class Customer { - - @Mixin - public static class placeOrder { // <1> - - private final Customer customer; - public documents(Customer customer) { this.customer = customer; } // <2> - - @Action - @ActionLayout(contributed = Contributed.AS_ACTION) - public List<Order> $$(Product p, int quantity) { // <3> - ... - } - public boolean hide$$() { ... } // <4> - public String validate0$$(Product p) { ... } - } -} ----- -<1> Prior to `1.13.2`, had to be prefixed by an "_"; this is no longer required because "$" is also recognized as a way of parsing the class name in order to infer the mixin's name (eg `Customer$placeOrder`). -<2> typically contributed to concrete class -<3> using the "$$" reserved name -<4> supporting methods as usual - - -Moreover, the mixin class can be capitalized if desired. -Thus: - -[source,java] ----- -public class Customer { - - @Mixin - public static class PlaceOrder { // <1> - - private final Customer customer; - public documents(Customer customer) { this.customer = customer; } // <2> - - @Action - @ActionLayout(contributed = Contributed.AS_ACTION) - public List<Order> $$(Product p, int quantity) { // <3> - ... - } - public boolean hide$$() { ... } // <4> - public String validate0$$(Product p) { ... } - } -} ----- - - -In other words, all of the following are allowed: - -* `public static class Documents { ... }` -* `public static class documents { ... }` -* `public static class _Documents { ... }` -* `public static class _documents { ... }` - -The reserved method name "$$" can also be changed using xref:../rgant/rgant.adoc#_rgant_Mixin_method[`@Mixin#method()`] or xref:../rgant/rgant.adoc#_rgant_DomainObject_mixinMethod[`@DomainObject#mixinMethod()`]. - - - - - - - - -== Programmatic usage - -When a domain object is rendered, the framework will automatically instantiate all required mixins and delegate to them -dynamically. If writing integration tests or fixtures, or (sometimes) just regular domain logic, then you may need to -instantiate mixins directly. - -For this you can use the -xref:../rgsvc/rgsvc.adoc#_rgsvc_api_DomainObjectContainer_object-creation-api[`DomainObjectContainer#mixin(...)` -method. For example: - -[source,java] ----- -DocumentHolder_documents mixin = container.mixin(DocumentHolder_documents.class, customer); ----- +include::_ugfun_programming-model_mixins_contributed-services .adoc[leveloffset=+1] -The xref:../ugtst/ugtst.adoc#__ugtst_integ-test-support_bootstrapping_IntegrationTestAbstract[`IntegrationTestAbstract`] and -xref:../rgcms/rgcms.adoc#_rgcms_classes_super_FixtureScript[`FixtureScript`] classes both provide a `mixin(...)` convenience -method. http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-action.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-action.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-action.adoc new file mode 100644 index 0000000..eec7692 --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-action.adoc @@ -0,0 +1,29 @@ +[[_ugfun_programming-model_mixins_contributed-action]] += Contributed Action + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + +Contributed actions are defined similarly, for example: + +[source,java] +---- +@Mixin(method="act") +public class DocumentHolder_addDocument { + + private final DocumentHolder holder; + public DocumentHolderDocuments(DocumentHolder holder) { this.holder = holder; } + + @Action() + @ActionLayout(contributed = Contributed.AS_ACTION) // <1> + public Document> act(Document doc) { + ... + } + public boolean hideAct() { ... } // <2> +} +---- +<1> recommended +<2> supporting methods follow the usual naming conventions. + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-collection.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-collection.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-collection.adoc new file mode 100644 index 0000000..d92f5d7 --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-collection.adoc @@ -0,0 +1,38 @@ +[[_ugfun_programming-model_mixins_contributed-collection]] += Contributed Collection + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + +The example below shows how to contribute a collection: + +[source,java] +---- +@Mixin(method="coll") +public class DocumentHolder_documents { + + private final DocumentHolder holder; + public DocumentHolderDocuments(DocumentHolder holder) { this.holder = holder; } + + @Action(semantics=SemanticsOf.SAFE) // <1> + @ActionLayout(contributed = Contributed.AS_ASSOCIATION) // <2> + @CollectionLayout(render = RenderType.EAGERLY) + public List<Document> coll() { // <3> + ... + } + public boolean hideColl() { ... } // <4> +} +---- +<1> required; actions that have side-effects cannot be contributed as collections +<2> required; otherwise the mixin will default to being rendered as an action +<3> must accept no arguments. +The mixin is a collection rather than a property because the return type is a collection, not a scalar. +<4> supporting methods follow the usual naming conventions. +(That said, in the case of collections, because the collection is derived/read-only, the only supporting method that is relevant is `hideColl()`). + +The above will result in a contributed collection "documents" for all types that implement/extend from `DocumentHolder`. + + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-property.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-property.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-property.adoc new file mode 100644 index 0000000..491a03f --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-property.adoc @@ -0,0 +1,35 @@ +[[_ugfun_programming-model_mixins_contributed-property]] += Contributed Property + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + +Contributed properties are defined similarly, for example: + +[source,java] +---- +@Mixin(method="prop") +public class DocumentHolder_mostRecentDocument { + + private final DocumentHolder holder; + public DocumentHolderDocuments(DocumentHolder holder) { this.holder = holder; } + + @Action(semantics=SemanticsOf.SAFE) // <1> + @ActionLayout(contributed = Contributed.AS_ASSOCIATION) // <2> + public Document prop() { // <3> + ... + } + public boolean hiderProp() { ... } // <4> +} +---- +<1> required; actions that have side-effects cannot be contributed as collections +<2> required; otherwise the mixin will default to being rendered as an action +<3> must accept no arguments. +The mixin is a property rather than a collection because the return type is a scalar. +<4> supporting methods follow the usual naming conventions. +(That said, in the case of properties, because the property is derived/read-only, the only supporting method that is relevant is `hideProp()`). + + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-services.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-services.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-services.adoc new file mode 100644 index 0000000..93ab3ce --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_contributed-services.adoc @@ -0,0 +1,46 @@ +[[_ugfun_programming-model_mixins_contributed-services]] += Contributed services (deprecated) + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + +Contributed services are very similar to mixins; indeed mixins are an evolution and refinement of the contributions concept. +As such, contributions should be considered as deprecated, and eventually removed in a future version of the framework, to be replaced entirely by mixins. + +The main difference between contributed services and mixins is that the actions of a contributed service will contribute to _all_ the parameters of its actions, whereas a mixin only contributes to the type accepted in its constructor. + +Also, contributed services are long-lived singletons, whereas mixins are instantiated as required (by the framework) and then discarded almost immediately. + +== Syntax + +Any n-parameter action provided by a service will automatically be contributed to the list of actions for each of its (entity) parameters. +From the viewpoint of the entity the action is called a contributed action. + +For example, given a service: + +[source,java] +---- +public interface Library { + public Loan borrow(Loanable l, Borrower b); +} +---- + +and the entities: + +[source,java] +---- +public class Book implements Loanable { ... } +---- + +and + +[source,java] +---- +public class LibraryMember implements Borrower { ... } +---- + +then the `borrow(...)` action will be contributed to both `Book` and to `LibraryMember`. + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_inferred-name.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_inferred-name.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_inferred-name.adoc new file mode 100644 index 0000000..9306218 --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_inferred-name.adoc @@ -0,0 +1,45 @@ +[[_ugfun_programming-model_mixins_inferred-name]] += Inferred Name + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + + +Where the mixin follows the naming convention `SomeType_mixinName` then the method name can be abbreviated, and the name of the member being contributed is inferred from the name of the class itself, beingeverything after the last '_'. + +The default abbreviation to "$$". + +For example: + +[source,java] +---- +@Mixin +public class DocumentHolder_documents { + + private final DocumentHolder holder; + public DocumentHolder_documents(DocumentHolder holder) { this.holder = holder; } + + @Action(semantics=SemanticsOf.SAFE) + @ActionLayout(contributed = Contributed.AS_ASSOCIATION) + @CollectionLayout(render = RenderType.EAGERLY) + public List<Document> $$() { // <1> + ... + } + public boolean hide$$() { ... } // <2> +} +---- +<1> using "$$" as the reserved method name +<2> supporting methods as usual + + +Alternatively, if the xref:../rgant/rgant.adoc#_rgant_Mixin_method[`@Mixin#method()`] attribute is specified, then this can nominate a different abbreviation. + +The examples above (for xref:../ugfun/ugfun.adoc#_ugfun_programming-model_mixins_contributed-property[property], xref:../ugfun/ugfun.adoc#_ugfun_programming-model_mixins_contributed-collection[collection] and xref:../ugfun/ugfun.adoc#_ugfun_programming-model_mixins_contributed-action[action]) demonstrate this. + + +The character "$" is also recognized as a separator between the mixin type and mixin name. +This is useful for mixins implemented as nested static types, discussed xref:../ugfun/ugfun.adoc#_ugfun_programming-model_mixins_nested-static-classes[below]. + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_nested-static-classes.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_nested-static-classes.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_nested-static-classes.adoc new file mode 100644 index 0000000..4596a2c --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_nested-static-classes.adoc @@ -0,0 +1,78 @@ +[[_ugfun_programming-model_mixins_nested-static-classes]] += As Nested Static Classes + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + +As noted in the introduction, while mixins were originally introduced as a means of allowing contributions from one module to the types of another module, they are also a convenient mechanism for grouping functionality/behaviour against a concrete type. +All the methods and supporting methods end up in a single construct, and the dependency between that functionality and the rest of the object is made more explicit. + +When using mixins in this fashion, it is idiomatic to write the mixin as a nested static class, using the naming convention described above to reduce duplication. + +For example: + +[source,java] +---- +public class Customer { + + @Mixin(method="act") + public static class placeOrder { // <1> + + private final Customer customer; + public documents(Customer customer) { this.customer = customer; } // <2> + + @Action + @ActionLayout(contributed = Contributed.AS_ACTION) + public List<Order> act(Product p, int quantity) { // <3> + ... + } + public boolean hideAct() { ... } // <4> + public String validate0Act(Product p) { ... } + } +} +---- +<1> Prior to `1.13.2`, had to be prefixed by an "_"; this is no longer required because "$" is also recognized as a way of parsing the class name in order to infer the mixin's name (eg `Customer$placeOrder`). +<2> typically contributed to concrete class +<3> using the "$$" reserved name +<4> supporting methods as usual + + +The mixin class can also be capitalized if desired. +Thus: + +[source,java] +---- +public class Customer { + + @Mixin(method="act") + public static class PlaceOrder { // <1> + + private final Customer customer; + public documents(Customer customer) { this.customer = customer; } // <2> + + @Action + @ActionLayout(contributed = Contributed.AS_ACTION) + public List<Order> act(Product p, int quantity) { // <3> + ... + } + public boolean hideAct() { ... } // <4> + public String validate0Act(Product p) { ... } + } +} +---- + + +In other words, all of the following are allowed: + +* `public static class Documents { ... }` +* `public static class documents { ... }` +* `public static class _Documents { ... }` +* `public static class _documents { ... }` + +The reserved method name "$$" can also be changed using xref:../rgant/rgant.adoc#_rgant_Mixin_method[`@Mixin#method()`] or xref:../rgant/rgant.adoc#_rgant_DomainObject_mixinMethod[`@DomainObject#mixinMethod()`]. + + + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_programmatic-usage.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_programmatic-usage.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_programmatic-usage.adoc new file mode 100644 index 0000000..862d816 --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_mixins_programmatic-usage.adoc @@ -0,0 +1,26 @@ +[[_ugfun_programming-model_mixins_programmatic-usage]] += Programmatic usage + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + +When a domain object is rendered, the framework will automatically instantiate all required mixins and delegate to them dynamically. +If writing integration tests or fixtures, or (sometimes) just regular domain logic, then you may need to instantiate mixins directly. + +For this you can use the xref:../rgsvc/rgsvc.adoc#_rgsvc_api_DomainObjectContainer_object-creation-api[`DomainObjectContainer#mixin(...)` method. + +For example: + +[source,java] +---- +DocumentHolder_documents mixin = container.mixin(DocumentHolder_documents.class, customer); +---- + +The xref:../ugtst/ugtst.adoc#__ugtst_integ-test-support_bootstrapping_IntegrationTestAbstract[`IntegrationTestAbstract`] and xref:../rgcms/rgcms.adoc#_rgcms_classes_super_FixtureScript[`FixtureScript`] classes both provide a `mixin(...)` convenience method. + + + + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_properties.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_properties.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_properties.adoc index 0ab9bad..b22a444 100644 --- a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_properties.adoc +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_properties.adoc @@ -129,7 +129,7 @@ If this is omitted then whether editing is enabled or disabled is defined global [[__ugfun_programming-model_properties_ignoring-properties]] == Ignoring Properties -By default Apache Isis will automatically render all properties in the xref:../ugvw/ugvw.adoc[UI] or in the xref:../ugvro/ugvro.adoc[REST API]. +By default Apache Isis will automatically render all properties in the xref:../ugvw/ugvw.adoc#[Wicket UI] or in the xref:../ugvro/ugvro.adoc[REST API]. To get Apache Isis to ignore a property (exclude it from its metamodel), annotate the getter using `@Programmatic`. Similarly, you can tell JDO/DataNucleus to ignore a property using the `@javax.jdo.annotations.NotPersistent` annotation. http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models.adoc index 37929da..0ff8781 100644 --- a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models.adoc +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models.adoc @@ -1,631 +1,28 @@ [[_ugfun_programming-model_view-models]] = View Models - :Notice: 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. :_basedir: ../../ :_imagesdir: images/ +As described in the xref:../ugfun/ugfun.adoc#\_ugfun_building-blocks_types-of-domain-objects_view-models[introduction], view models are domain objects like domain entities, with behaviour and state. +However, unlike domain entities, their state is _not_ persisted to a database but is instead serialized into its identifier (in effect, its URL). +The framework provides two main ways to implement a view model: -NOTE: FIXME - move the stuff from ugbtb here - -NOTE: FIXME - deprecate the @ViewModel stuff, emphasise using JAXB only - - - - -[[__ugfun_building-blocks_view-models_typical-implementation]] -== Typical Implementation - -Apache Isis offers several ways to implement view models, but the most flexible/powerful is to annotate the class using JAXB annotations. -For example: - -[source,java] ----- -@XmlRootElement(name = "invoiceRun") // <1> -@XmlType( - propOrder = { // <2> - ... - } -) -public class InvoiceRun { - ... -} ----- -<1> The JAXB `@XmlRootElement` annotation indicates this is a view model to Apache Isis, which then uses JAXB to serialize the state of the view model between interactions -<2> All properties of the view model must be listed using the `XmlType#propOrder` attribute. - -Use JAXB elements such as `@XmlElement` for properties and the combination of `@XmlElementWrapper` and `@XmlElement` for collections. -Properties can be ignored (for serialization) using `@XmlTransient`. - - -'''' -'''' - -[[_ugbtb_view-models_jaxb]] -== JAXB-annotated View Models/DTOs - -As noted in the xref:../ugfun/ugfun.adoc#_ugfun_building-blocks_view-models[introduction], view models can also be defined using JAXB annotations. -The serialized form of these view models is therefore XML, which also enables these view models -to act as DTOs. - -In case it's not obvious, these DTOs are still usable as "regular" view models; they will render in the xref:../ugvw/ugvw.adoc#[Wicket viewer] just like any other. -In fact, these JAXB-annotated view models are in many regards the most powerful of all the various ways of writing view models: - -* their entire state (collections as well as properties) is automatically managed from interaction to interaction. + -+ -In contrast, using xref:../rgant/rgant.adoc#_rgant-ViewModel[`@ViewModel`] (or its xref:../rgant/rgant.adoc#_rgant-DomainObject_nature[`@DomainObject#nature()`] equivalent) will only manage the state of properties, but not collections. -And if using the xref:../rgcms/rgcms.adoc#_rgcms_classes_super_ViewModel[`ViewModel`] interface, then the programmer must write all the state management (lots of boilerplate). - -* JAXB-annotated view models are "in effect" editable. - -The examples in this section uses the DTO for `ToDoItem`, taken from the (non-ASF) http://github.com/isisaddons/isis-app-todoapp[Isis addons' todoapp]. -This DTO is defined as follows: - -[source,java] ----- -package todoapp.app.viewmodels.todoitem.v1; // <1> -@XmlRootElement(name = "toDoItemDto") // <2> -@XmlType( - propOrder = { // <3> - "majorVersion", "minorVersion", - "description", "category", ... - "toDoItem", "similarItems" - } -) -@DomainObjectLayout( - titleUiEvent = TitleUiEvent.Doop.class // <4> -) -public class ToDoItemV1_1 implements Dto { // <5> - @XmlElement(required = true, defaultValue = "1") // <6> - public final String getMajorVersion() { return "1"; } - @XmlElement(required = true, defaultValue = "1") // <7> - public String getMinorVersion() { return "1"; } - - @XmlElement(required = true) // <8> - @Getter @Setter - protected String description; - @XmlElement(required = true) - @Getter @Setter - protected String category; - ... - - @Getter @Setter // <9> - protected ToDoItem toDoItem; - @XmlElementWrapper // <10> - @XmlElement(name = "todoItem") - @Getter @Setter - protected List<ToDoItem> similarItems = Lists.newArrayList(); -} ----- -<1> package name encodes major version; see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] -<2> identifies this class as a view model and defines the root element for JAXB serialization -<3> all properties in the class must be listed; (they can be ignored using `@XmlTransient`) -<4> demonstrating use of UI events for a subscriber to provide the DTO's title; see xref:../rgant/rgant.adoc#_rgant-DomainObjectLayout_titleUiEvent[`@DomainObjectLayout#titleUiEvent()`]. -<5> class name encodes (major and) minor version; see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] -<6> again, see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] -<7> again, see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] -<8> simple scalar properties -<9> reference to a persistent entity; discussed xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_referencing-domain-entities[below] -<10> reference to a collection of persistent entities; again discussed xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_referencing-domain-entities[below] - - - -[[__ugbtb_view-models_jaxb_referencing-domain-entities]] -=== Referencing Domain Entities - -It's quite common for view models to be "backed by" (be projections of) some underlying domain entity. -The `ToDoItemDto` we've been using as the example in this section is an example: there is an underlying `ToDoItem` entity. - -It wouldn't make sense to serialize out the state of a persistent entity: the point of a DTO is to act as a facade on top of the entity so that the implementation details (of the entity's structure) don't leak out to the consumer. -However, the identity of the underlying entity can be well defined; Apache Isis defines the xref:../rgcms/rgcms.adoc#_rgcms_schema-common[Common schema] which defines the `<oid-dto>` element (and corresponding `OidDto` class): the object's type and its identifier. -This is basically a formal XML equivalent to the `Bookmark` object obtained from the xref:../rgsvc/rgsvc.adoc#_rgsvc_api_BookmarkService[`BookmarkService`]. - -There is only one requirement to make this work: every referenced domain entity must be annotated with xref:../rgant/rgant.adoc#_rgant-XmlJavaTypeAdapter[`@XmlJavaTypeAdapter`], specifying the framework-provided `PersistentEntityAdapter.class`. -This class is similar to the `BookmarkService`: it knows how to create an `OidDto` from an object reference. - -Thus, in our view model we can legitimately write: - -[source,java] ----- -package todoapp.app.viewmodels.todoitem.v1; -... -public class ToDoItemV1_1 implements Dto { - ... - @Getter @Setter - protected ToDoItem toDoItem; -} ----- - -All we need to do is remember to add that `@XmlJavaTypeAdapter` annotation to the referenced entity: - -[source,java] ----- -@XmlJavaTypeAdapter(PersistentEntityAdapter.class) -public class ToDoItem ... { - ... -} ----- - - -It's also possible for a DTO to hold collections of objects. -These can be of any type, either simple properties, or references to other objects. -The only bit of boilerplate that is required is the `@XmlElementWrapper` annotation. -This instructs JAXB to create an XML element (based on the field name) to contain each of the elements. -(If this is omitted then the contents of the collection are at the same level as the properties; almost certainly not what is required). - -For example, the DTO also contains: - -[source,java] ----- -package todoapp.app.viewmodels.todoitem.v1; -... -public class ToDoItemV1_1 implements Dto { - ... - @XmlElementWrapper - @XmlElement(name = "todoItem") - @Getter @Setter - protected List<ToDoItem> similarItems = Lists.newArrayList(); -} ----- - - -There's nothing to prevent a JAXB DTO from containing rich graphs of data, parent containing children containing children. -Be aware though that all of this state will become the DTO's memento, ultimately converted into a URL-safe form, by way of the xref:../rgsvc/rgsvc.adoc#_rgsvc_spi_UrlEncodingService[`UrlEncodingService`]. - -There are limits to the lengths of URLs, however. -Therefore the DTO should not include state that can easily be derived from other information. -If the URL does exceed limits, then provide a custom implementation of xref:../rgsvc/rgsvc.adoc#_rgsvc_spi_UrlEncodingService[`UrlEncodingService`] to handle the memento string in some other fashion (eg substituting it with a GUID, with the memento cached somehow on the server). - - - - - -[[__ugbtb_view-models_jaxb_versioning]] -=== Versioning - -The whole point of using DTOs (in Apache Isis, at least) is to define a formal contact between two inter-operating but independent applications. -Since the only thing we can predicate about the future with any certainty is that it one or both of these applications will change, we should version DTOs from the get-go. -This allows us to make changes going forward without unnecessarily breaking existing consumers of the data. - -[NOTE] -==== -There are several ways that versioning might be accomplished; we base our guidelines on this link:http://www.xfront.com/Versioning.pdf[article] taken from Roger Costello's blog, well worth a read. -==== - -We can distinguish two types of changes: - -* backwardly compatible changes -* breaking changes. - -We can immediately say that the XSD namespace should change only when there is a major/breaking change, if following link:http://semver.org[semantic versioning] that means when we bump the major version number v1, v2, etc. - -XML namespaces correspond (when using JAXB) to Java packages. -We should therefore place our DTOs in a package that contains only the major number; this package will eventually contain a range of DTOs that are intended to be backwardly compatible with one another. -The package should also have a `package-info.java`; it is this that declares the XSD namespace: - -[source,java] ----- -@javax.xml.bind.annotation.XmlSchema( - namespace = "http://viewmodels.app.todoapp/todoitem/v1/Dto.xsd", // <1> - xmlns = { - @javax.xml.bind.annotation.XmlNs( - namespaceURI = "http://isis.apache.org/schema/common", - prefix = "com" - ) - }, - elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED -) -package todoapp.app.viewmodels.todoitem.v1; // <2> ----- -<1> the namespace URI, used by the DTO residing in this package. -<2> the package in which the DTO resides. Note that this contains only the major version. - -Although there is no requirement for the namespace URI to correspond to a physical URL, it should be unique. -This usually means including a company domain name within the string. - -As noted above, this package will contain multiple DTO classes all with the same namespace; these represent a set of minor versions of the DTO, each subsequent one intended to be backwardly compatible with the previous. -Since these DTO classes will all be in the same package (as per the xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_using-packages-to-version[advice above]), the class should therefore include the minor version name: - -[source,java] ----- -package todoapp.app.viewmodels.todoitem.v1; // <1> -... -public class ToDoItemV1_1 implements Dto { // <2> - ... -} ----- -<1> package contains the major version only -<2> DTO class contains the (major and) minor version - - -We also recommend that each DTO instance should also specify the version of the XSD schema that it is logically compatible with. -Probably most consumers will not persist the DTOs; they will be processed and then discarded. -However, it would be wrong to assume that is the case in all cases; some consumers might choose to persist the DTO (eg for replay at some later state). - -Thus: - -[source,java] ----- -public class ToDoItemV1_1 implements Dto { - @XmlElement(required = true, defaultValue = "1") - public final String getMajorVersion() { return "1"; } // <1> - @XmlElement(required = true, defaultValue = "1") - public String getMinorVersion() { return "1"; } // <2> - ... -} ----- -<1> returns the major version (in sync with the package) -<2> returns the minor version (in sync with the class name) - -These methods always return a hard-coded literal. -Any instances serialized from these classes will implicitly "declare" the (major and) minor version of the schema with which they are compatible. -If a consumer has a minimum version that it requires, it can therefore inspect the XML instance itself to determine if it is able to consume said XML. - -If a new (minor) version of a DTO is required, then we recommend copying-and-pasting the previous version, eg: - -[source,java] ----- -public class ToDoItemV1_2 implements Dto { - @XmlElement(required = true, defaultValue = "1") - public final String getMajorVersion() { return "1"; } - @XmlElement(required = true, defaultValue = "2") - public String getMinorVersion() { return "2"; } - ... -} ----- - -Obviously, only changes made must be backward compatible, eg new members must be optional. - -Alternatively, you might also consider simply editing the source file, ie renaming the class and bumping up the value returned by `getMinorVersion()`. - -We also _don't_ recommend using inheritance (ie `ToDoItemV1_2` should not inherit from `ToDoItemV1_1`; this creates unnecessary complexity downstream if generating XSDs and DTOs for the downstream consumer. - - -[[__ugbtb_view-models_jaxb_generating-xsds-and-dtos]] -=== Generating XSDs and DTOs - -In the section xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_referencing-domain-entities[above] it was explained how a view model DTO can transparent reference any "backing" entities; these references are converted to internal object identifiers. - -However, if the consumer of the XML is another Java process (eg running within an Apache Camel route), then you might be tempted/expect to be able to use the same DTO within that Java process. -After a little thought though you'll realize that (duh!) of course you cannot; the consumer runs in a different process space, and will not have references to those containing entities. - -There are therefore two options: - -* either choose not to have the view model DTO reference any persistent entities, and simply limit the DTO to simple scalars. + -+ -Such a DTO will then be usable in both the Apache Isis app (to generate the original XML) and in the consumer. -The xref:../rgsvc/rgsvc.adoc#_rgsvc_api_BookmarkService[`BookmarkService`] can be used to obtain the object identifiers - -* alternatively, generate a different DTO for the consumer from the XSD of the view model DTO. - -The (non-ASF) http://github.com/isisaddons/isis-app-todoapp[Isis addons' todoapp] uses the second approach; generating the XSD and consumer's DTO is mostly just boilerplate `pom.xml` file. -In the todoapp this can be found in the `todoapp-xsd` Maven module, whose `pom.xml` is structured as two profiles: - -[source,xml] ----- -<project ... > - <artifactId>todoapp-xsd</artifactId> - <dependencies> - <dependency> - <groupId>${project.groupId}</groupId> - <artifactId>todoapp-app</artifactId> - </dependency> - </dependencies> - <profiles> - <profile> - <id>isis-xsd</id> - <activation> - <property> - <name>!skip.isis-xsd</name> - </property> - </activation> - ... - </profile> - <profile> - <id>xjc</id> - <activation> - <property> - <name>!skip.xjc</name> - </property> - </activation> - ... - </profile> - </profiles> -</project> ----- - -The `isis-xsd` profile generates the XSD using the xref:../rgmvn/rgmvn.adoc#_rgmvn_xsd[`xsd` goal] of Isis' maven plugin: - -[source,xml] ----- -<build> - <plugins> - <plugin> - <groupId>org.apache.isis.tool</groupId> - <artifactId>isis-maven-plugin</artifactId> - <version>${isis.version}</version> - <configuration> - <appManifest>todoapp.dom.ToDoAppDomManifest</appManifest> - <jaxbClasses> - <jaxbClass>todoapp.app.viewmodels.todoitem.v1.ToDoItemV1_1</jaxbClass> - </jaxbClasses> - <separate>false</separate> - <commonSchemas>false</commonSchemas> - </configuration> - <dependencies> - <dependency> - <groupId>${project.groupId}</groupId> - <artifactId>todoapp-dom</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.google.guava</groupId> - <artifactId>guava</artifactId> - <version>16.0.1</version> - </dependency> - </dependencies> - <executions> - <execution> - <phase>generate-sources</phase> - <goals> - <goal>xsd</goal> - </goals> - </execution> - </executions> - </plugin> - <plugin> - <artifactId>maven-assembly-plugin</artifactId> - <version>2.5.3</version> - <configuration> - <descriptor>src/assembly/dep.xml</descriptor> - </configuration> - <executions> - <execution> - <id>create-archive</id> - <phase>package</phase> - <goals> - <goal>single</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> -</build> ----- - -The `todoapp.dom.ToDoAppDomManifest` is a cut-down version of the app manifest that identifies only the `dom` domain services. - -The `xjc` profile, meanwhile, uses the `maven-jaxb2-plugin` (a wrapper around the `schemagen` JDK tool) to generate a DTO from the XSD generated by the preceding profile: - -[source,xml] ----- -<build> - <plugins> - <plugin> - <groupId>org.jvnet.jaxb2.maven2</groupId> - <artifactId>maven-jaxb2-plugin</artifactId> - <version>0.12.3</version> - <executions> - <execution> - <id>xjc-generate</id> - <phase>generate-sources</phase> - <goals> - <goal>generate</goal> - </goals> - </execution> - </executions> - <configuration> - <removeOldOutput>true</removeOldOutput> - <schemaDirectory> - target/generated-resources/isis-xsd/viewmodels.app.todoapp - </schemaDirectory> - <schemaIncludes> - <schemaInclude>todoitem/v1/Dto.xsd</schemaInclude> - </schemaIncludes> - <bindingDirectory>src/main/resources</bindingDirectory> - <bindingIncludes> - <bindingInclude>binding.xml</bindingInclude> - </bindingIncludes> - <catalog>src/main/resources/catalog.xml</catalog> - </configuration> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>build-helper-maven-plugin</artifactId> - <version>1.9.1</version> - <executions> - <execution> - <id>add-source</id> - <phase>generate-sources</phase> - <goals> - <goal>add-source</goal> - </goals> - <configuration> - <sources> - <source>target/generated-sources/xjc</source> - </sources> - </configuration> - </execution> - </executions> - </plugin> - </plugins> -</build> ----- - - -'''' -'''' - -== (Non-JAXB) View Models - -NOTE: FIXME - consider this as deprecated - -This section describes how to implement view models. - -Fundamentally all view models' state is serialized into -a string memento; this memento is then held by the client (browser) in the form of a URL. As you might imagine, this -URL can become quite long, but Apache Isis offers a mechanism (the xref:../rgsvc/rgsvc.adoc#_rgsvc_spi_UrlEncodingService[`UrlEncodingService`]) if it exceeds the maximum length for a URL -(2083 characters). Also, of course, this string memento must only contain characters that it is valid for use within -a URL. - -While the underlying technique is the same irrespective of use case, the programming model provides various ways of -defining a view model so that the original intent is not lost. They are: - -.View model programming model -[cols="1a,4a,2a", options="header"] -|=== - -| Use case -| Code -| Description - - -| External entity -|[source,java] ----- -@DomainObject(nature=Nature.EXTERNAL_ENTITY) -public class CustomerRecordOnSAP { ... } ----- -|Annotated with xref:../rgant/rgant.adoc#_rgant-DomainObject_nature[`@DomainObject#nature()`] and a nature of `EXTERNAL_ENTITY`, with memento derived automatically from the properties of the domain object. Collections are ignored, as are any properties annotated as xref:../rgant/rgant.adoc#_rgant-Property_notPersisted[not persisted]. - -| In-memory entity -|[source,java] ----- -@DomainObject(nature=Nature.INMEMORY_ENTITY) -public class Log4JAppender { ... } ----- -|As preceding, but using a nature of `INMEMORY_ENTITY`. - -|Application view model -|[source,java] ----- -@DomainObject(nature=Nature.VIEW_MODEL) -public class Dashboard { ... } ----- -|As preceding, but using a nature of `VIEW_MODEL`. - -|Application view model -| -[source,java] ----- -@ViewModel -public class Dashboard { ... } ----- - -|Annotated with xref:../rgant/rgant.adoc#_rgant-ViewModel[`@ViewModel`] annotation (effectively just an alias)' memento is as preceding: from "persisted" properties, collections ignored - -|Application view model -| -[source,java] ----- -public class ExcelUploadManager implements ViewModel { - public String viewModelMemento() { ... } - public void viewModelInit(String memento) { ... } -} -|Implement xref:../rgcms/rgcms.adoc#_rgcms_classes_super_ViewModel[`ViewModel`] interface. The memento is as defined by the -interface's methods: the programmer has full control (but also full responsibility) for the string memento. - -|DTO -| -[source,java] ----- -@XmlRootElement("customer") -public class CustomerDto { ... } ----- -|Annotate using JAXB xref:../rgant/rgant.adoc#_rgant-XmlRootElement[`@XmlRootElement`] annotation. Memento -derived automatically by serializing the XML graph as implied by the JAXB annotations. Note that (unlike `@ViewModel` -et al) this state _can_ include collections. -|=== - -JAXB-annotated DTOs are discussed in more detail immediately xref:rg.adoc#_ugbtb_view-models_jaxb[below]. - - - -'''' -'''' - - - -This section describes how to implement view models. - -Fundamentally all view models' state is serialized into -a string memento; this memento is then held by the client (browser) in the form of a URL. As you might imagine, this -URL can become quite long, but Apache Isis offers a mechanism (the xref:../rgsvc/rgsvc.adoc#_rgsvc_spi_UrlEncodingService[`UrlEncodingService`]) if it exceeds the maximum length for a URL -(2083 characters). Also, of course, this string memento must only contain characters that it is valid for use within -a URL. - -While the underlying technique is the same irrespective of use case, the programming model provides various ways of -defining a view model so that the original intent is not lost. They are: - -.View model programming model -[cols="1a,4a,2a", options="header"] -|=== - -| Use case -| Code -| Description - - -| External entity -|[source,java] ----- -@DomainObject(nature=Nature.EXTERNAL_ENTITY) -public class CustomerRecordOnSAP { ... } ----- -|Annotated with xref:../rgant/rgant.adoc#_rgant-DomainObject_nature[`@DomainObject#nature()`] and a nature of `EXTERNAL_ENTITY`, with memento derived automatically from the properties of the domain object. Collections are ignored, as are any properties annotated as xref:../rgant/rgant.adoc#_rgant-Property_notPersisted[not persisted]. - -| In-memory entity -|[source,java] ----- -@DomainObject(nature=Nature.INMEMORY_ENTITY) -public class Log4JAppender { ... } ----- -|As preceding, but using a nature of `INMEMORY_ENTITY`. - -|Application view model -|[source,java] ----- -@DomainObject(nature=Nature.VIEW_MODEL) -public class Dashboard { ... } ----- -|As preceding, but using a nature of `VIEW_MODEL`. - -|Application view model -| -[source,java] ----- -@ViewModel -public class Dashboard { ... } ----- - -|Annotated with xref:../rgant/rgant.adoc#_rgant-ViewModel[`@ViewModel`] annotation (effectively just an alias)' memento is as preceding: from "persisted" properties, collections ignored +* The more powerful/flexible approach is to use JAXB annotations; this allows the state of the object's properties and also its collections. -|Application view model -| -[source,java] ----- -public class ExcelUploadManager implements ViewModel { - public String viewModelMemento() { ... } - public void viewModelInit(String memento) { ... } -} -|Implement xref:../rgcms/rgcms.adoc#_rgcms_classes_super_ViewModel[`ViewModel`] interface. The memento is as defined by the -interface's methods: the programmer has full control (but also full responsibility) for the string memento. +* The other approach is to use Apache Isis specific annotations. +While (arguably) these explain the intent of the view model better, they are more restrictive: only the state of the object's properties is serialized -- collections are ignored -- and not every datatype is recognized. -|DTO -| -[source,java] ----- -@XmlRootElement("customer") -public class CustomerDto { ... } ----- -|Annotate using JAXB xref:../rgant/rgant.adoc#_rgant-XmlRootElement[`@XmlRootElement`] annotation. Memento -derived automatically by serializing the XML graph as implied by the JAXB annotations. Note that (unlike `@ViewModel` -et al) this state _can_ include collections. -|=== +The serialized form of these view models is therefore XML, which also enables these view models to act as DTO (useful for various integration scenarios). -JAXB-annotated DTOs are discussed in more detail immediately xref:rg.adoc#_ugbtb_view-models_jaxb[below]. +For these reasons we recommend that you use JAXB-style view models wherever possible. +Indeed, the legacy approach for view models may be deprecated in the future. +In the sections below we consider JAXB view models both as "regular" view models, and also when using them to act as DTOs. +include::_ugfun_programming-model_view-models_jaxb.adoc[leveloffset=+1] +include::_ugfun_programming-model_view-models_dto.adoc[leveloffset=+1] +include::_ugfun_programming-model_view-models_non-jaxb.adoc[leveloffset=+1] http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto.adoc new file mode 100644 index 0000000..ad5b8fd --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto.adoc @@ -0,0 +1,69 @@ +[[_ugfun_programming-model_view-models_dto]] += DTOs + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + +JAXB view models can also be used as DTOs. +The examples in this section uses the DTO for `ToDoItem`, taken from the (non-ASF) http://github.com/isisaddons/isis-app-todoapp[Isis addons' todoapp]. + +This DTO is defined as follows: + +[source,java] +---- +package todoapp.app.viewmodels.todoitem.v1; // <1> +@XmlRootElement(name = "toDoItemDto") // <2> +@XmlType( + propOrder = { // <3> + "majorVersion", "minorVersion", + "description", "category", ... + "toDoItem", "similarItems" + } +) +@DomainObjectLayout( + titleUiEvent = TitleUiEvent.Doop.class // <4> +) +public class ToDoItemV1_1 implements Dto { // <5> + @XmlElement(required = true, defaultValue = "1") // <6> + public final String getMajorVersion() { return "1"; } + @XmlElement(required = true, defaultValue = "1") // <7> + public String getMinorVersion() { return "1"; } + + @XmlElement(required = true) // <8> + @Getter @Setter + protected String description; + @XmlElement(required = true) + @Getter @Setter + protected String category; + ... + + @Getter @Setter // <9> + protected ToDoItem toDoItem; + @XmlElementWrapper // <10> + @XmlElement(name = "todoItem") + @Getter @Setter + protected List<ToDoItem> similarItems = Lists.newArrayList(); +} +---- +<1> package name encodes major version; see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] +<2> identifies this class as a view model and defines the root element for JAXB serialization +<3> all properties in the class must be listed; (they can be ignored using `@XmlTransient`) +<4> demonstrating use of UI events for a subscriber to provide the DTO's title; see xref:../rgant/rgant.adoc#_rgant-DomainObjectLayout_titleUiEvent[`@DomainObjectLayout#titleUiEvent()`]. +<5> class name encodes (major and) minor version; see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] +<6> again, see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] +<7> again, see discussion on xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_versioning[versioning] +<8> simple scalar properties +<9> reference to a persistent entity; discussed xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_referencing-domain-entities[below] +<10> reference to a collection of persistent entities; again discussed xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_referencing-domain-entities[below] + + + + + +include::_ugfun_programming-model_view-models_dto_versioning.adoc[leveloffset=+1] + +include::_ugfun_programming-model_view-models_dto_generating-xsds.adoc[leveloffset=+1] + +include::_ugfun_programming-model_view-models_dto_dto-consumers.adoc[leveloffset=+1] http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_dto-consumers.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_dto-consumers.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_dto-consumers.adoc new file mode 100644 index 0000000..5d1e22d --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_dto-consumers.adoc @@ -0,0 +1,32 @@ +[[_ugfun_programming-model_view-models_dto_dto-consumers.adoc]] += DTO Consumers +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + +The actual consumers of DTOs will generally obtain the XML of the view models either by requesting the XML directly, eg using the xref:../ugvro/ugvro.adoc#[RestfulObjects viewer], or may have the XML sent to them asynchronously using an ESB such as Apache Camel. + +In the former case, the consumer requests the DTO by calling the REST API with the appropriate HTTP `Accept` header. +An appropriate implementation of xref:../rgsvc/rgsvc.adoc#_rgsvc_spi_ContentMappingService[`ContentMappingService`] can then be used to return the appropriate DTO (as XML). + +For the latter case, one design is simply for the application to instantiate the view model, then call the xref:../rgsvc/rgsvc.adoc#_rgsvc_api_JaxbService[`JaxbService`] to obtain its corresponding XML. This can then be published onto the ESB, for example using an http://activemq.apache.org[Apache ActiveMQ (TM)] queue. + +However, rather than try to push all the data that might be needed by any of these external systems in a single XML event (which would require anticipating all the requirements, likely a hopeless task), a better design is to publish only the fact that something of note has changed - ie, that an action on a domain object has been invoked - and then let the consumers call back to obtain other information if required. +This can once again be done by calling the REST API with an appropriate HTTP `Accept` header. + +[TIP] +==== +This is an example of the link:https://leanpub.com/camel-design-patterns[VETRO pattern] (validate, enrich, transform, route, operate). +In our case we focus on the validation (to determine the nature of the inbound message, ie which action was invoked), and the enrich (callback to obtain a DTO with additional information required by the consumer). +==== + +The (non-ASF) http://github.com/isisaddons/isis-module-publishmq[Isis addons' publishmq] module provides an out-of-the-box solution of this design. +It provides an implementation of the xref:../rgsvc/rgsvc.adoc#_rgsvc_spi_PublishingService[`PublishingService`], but which simply publishes instances of xref:../rgcms/rgcms.adoc#_rgcms_schema-aim[`ActionInvocationMemento`] to an ActiveMQ queue. +Camel (or similar) can then be hooked up to consume these events from this queue, and use a processor to parse the action memento to determine what has changed on the source system. +Thereafter, a subsequent Camel processor can then call back to the source - via the xref:../ugvro/ugvro.adoc#[Restful Objects viewer] - to enrich the message with additional details using a DTO. + + + + http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_generating-xsds.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_generating-xsds.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_generating-xsds.adoc new file mode 100644 index 0000000..e040f43 --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_generating-xsds.adoc @@ -0,0 +1,176 @@ +[[_ugfun_programming-model_view-models_dto_generating-xsds]] += Generating XSDs and DTOs +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + + + +In the section xref:../ugfun/ugfun.adoc#_ugfun_programming-model_view-models_jaxb_referencing-domain-entities[above] it was explained how a view model DTO can transparent reference any "backing" entities; these references are converted to internal object identifiers. + +However, if the consumer of the XML is another Java process (eg running within an Apache Camel route), then you might be tempted/expect to be able to use the same DTO within that Java process. +After a little thought though you'll realize that (duh!) of course you cannot; the consumer runs in a different process space, and will not have references to those containing entities. + +There are therefore two options: + +* either choose not to have the view model DTO reference any persistent entities, and simply limit the DTO to simple scalars. + ++ +Such a DTO will then be usable in both the Apache Isis app (to generate the original XML) and in the consumer. +The xref:../rgsvc/rgsvc.adoc#_rgsvc_api_BookmarkService[`BookmarkService`] can be used to obtain the object identifiers + +* alternatively, generate a different DTO for the consumer from the XSD of the view model DTO. + +The (non-ASF) http://github.com/isisaddons/isis-app-todoapp[Isis addons' todoapp] uses the second approach; generating the XSD and consumer's DTO is mostly just boilerplate `pom.xml` file. +In the todoapp this can be found in the `todoapp-xsd` Maven module, whose `pom.xml` is structured as two profiles: + +[source,xml] +---- +<project ... > + <artifactId>todoapp-xsd</artifactId> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>todoapp-app</artifactId> + </dependency> + </dependencies> + <profiles> + <profile> + <id>isis-xsd</id> + <activation> + <property> + <name>!skip.isis-xsd</name> + </property> + </activation> + ... + </profile> + <profile> + <id>xjc</id> + <activation> + <property> + <name>!skip.xjc</name> + </property> + </activation> + ... + </profile> + </profiles> +</project> +---- + +The `isis-xsd` profile generates the XSD using the xref:../rgmvn/rgmvn.adoc#_rgmvn_xsd[`xsd` goal] of Isis' maven plugin: + +[source,xml] +---- +<build> + <plugins> + <plugin> + <groupId>org.apache.isis.tool</groupId> + <artifactId>isis-maven-plugin</artifactId> + <version>${isis.version}</version> + <configuration> + <appManifest>todoapp.dom.ToDoAppDomManifest</appManifest> + <jaxbClasses> + <jaxbClass>todoapp.app.viewmodels.todoitem.v1.ToDoItemV1_1</jaxbClass> + </jaxbClasses> + <separate>false</separate> + <commonSchemas>false</commonSchemas> + </configuration> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>todoapp-dom</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>16.0.1</version> + </dependency> + </dependencies> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>xsd</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.5.3</version> + <configuration> + <descriptor>src/assembly/dep.xml</descriptor> + </configuration> + <executions> + <execution> + <id>create-archive</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> +</build> +---- + +The `todoapp.dom.ToDoAppDomManifest` is a cut-down version of the app manifest that identifies only the `dom` domain services. + +The `xjc` profile, meanwhile, uses the `maven-jaxb2-plugin` (a wrapper around the `schemagen` JDK tool) to generate a DTO from the XSD generated by the preceding profile: + +[source,xml] +---- +<build> + <plugins> + <plugin> + <groupId>org.jvnet.jaxb2.maven2</groupId> + <artifactId>maven-jaxb2-plugin</artifactId> + <version>0.12.3</version> + <executions> + <execution> + <id>xjc-generate</id> + <phase>generate-sources</phase> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + <configuration> + <removeOldOutput>true</removeOldOutput> + <schemaDirectory> + target/generated-resources/isis-xsd/viewmodels.app.todoapp + </schemaDirectory> + <schemaIncludes> + <schemaInclude>todoitem/v1/Dto.xsd</schemaInclude> + </schemaIncludes> + <bindingDirectory>src/main/resources</bindingDirectory> + <bindingIncludes> + <bindingInclude>binding.xml</bindingInclude> + </bindingIncludes> + <catalog>src/main/resources/catalog.xml</catalog> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.9.1</version> + <executions> + <execution> + <id>add-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>target/generated-sources/xjc</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> +</build> +---- http://git-wip-us.apache.org/repos/asf/isis/blob/85b1e70b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_versioning.adoc ---------------------------------------------------------------------- diff --git a/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_versioning.adoc b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_versioning.adoc new file mode 100644 index 0000000..2c61f4e --- /dev/null +++ b/adocs/documentation/src/main/asciidoc/guides/ugfun/_ugfun_programming-model_view-models_dto_versioning.adoc @@ -0,0 +1,106 @@ +[[_ugfun_programming-model_view-models_dto_versioning]] += Versioning + +:Notice: 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. +:_basedir: ../../ +:_imagesdir: images/ + + +The whole point of using DTOs (in Apache Isis, at least) is to define a formal contact between two inter-operating but independent applications. +Since the only thing we can predicate about the future with any certainty is that it one or both of these applications will change, we should version DTOs from the get-go. +This allows us to make changes going forward without unnecessarily breaking existing consumers of the data. + +[NOTE] +==== +There are several ways that versioning might be accomplished; we base our guidelines on this link:http://www.xfront.com/Versioning.pdf[article] taken from Roger Costello's blog, well worth a read. +==== + +We can distinguish two types of changes: + +* backwardly compatible changes +* breaking changes. + +We can immediately say that the XSD namespace should change only when there is a major/breaking change, if following link:http://semver.org[semantic versioning] that means when we bump the major version number v1, v2, etc. + +XML namespaces correspond (when using JAXB) to Java packages. +We should therefore place our DTOs in a package that contains only the major number; this package will eventually contain a range of DTOs that are intended to be backwardly compatible with one another. +The package should also have a `package-info.java`; it is this that declares the XSD namespace: + +[source,java] +---- +@javax.xml.bind.annotation.XmlSchema( + namespace = "http://viewmodels.app.todoapp/todoitem/v1/Dto.xsd", // <1> + xmlns = { + @javax.xml.bind.annotation.XmlNs( + namespaceURI = "http://isis.apache.org/schema/common", + prefix = "com" + ) + }, + elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED +) +package todoapp.app.viewmodels.todoitem.v1; // <2> +---- +<1> the namespace URI, used by the DTO residing in this package. +<2> the package in which the DTO resides. Note that this contains only the major version. + +Although there is no requirement for the namespace URI to correspond to a physical URL, it should be unique. +This usually means including a company domain name within the string. + +As noted above, this package will contain multiple DTO classes all with the same namespace; these represent a set of minor versions of the DTO, each subsequent one intended to be backwardly compatible with the previous. +Since these DTO classes will all be in the same package (as per the xref:../ugbtb/ugbtb.adoc#__ugbtb_view-models_jaxb_using-packages-to-version[advice above]), the class should therefore include the minor version name: + +[source,java] +---- +package todoapp.app.viewmodels.todoitem.v1; // <1> +... +public class ToDoItemV1_1 implements Dto { // <2> + ... +} +---- +<1> package contains the major version only +<2> DTO class contains the (major and) minor version + + +We also recommend that each DTO instance should also specify the version of the XSD schema that it is logically compatible with. +Probably most consumers will not persist the DTOs; they will be processed and then discarded. +However, it would be wrong to assume that is the case in all cases; some consumers might choose to persist the DTO (eg for replay at some later state). + +Thus: + +[source,java] +---- +public class ToDoItemV1_1 implements Dto { + @XmlElement(required = true, defaultValue = "1") + public final String getMajorVersion() { return "1"; } // <1> + @XmlElement(required = true, defaultValue = "1") + public String getMinorVersion() { return "1"; } // <2> + ... +} +---- +<1> returns the major version (in sync with the package) +<2> returns the minor version (in sync with the class name) + +These methods always return a hard-coded literal. +Any instances serialized from these classes will implicitly "declare" the (major and) minor version of the schema with which they are compatible. +If a consumer has a minimum version that it requires, it can therefore inspect the XML instance itself to determine if it is able to consume said XML. + +If a new (minor) version of a DTO is required, then we recommend copying-and-pasting the previous version, eg: + +[source,java] +---- +public class ToDoItemV1_2 implements Dto { + @XmlElement(required = true, defaultValue = "1") + public final String getMajorVersion() { return "1"; } + @XmlElement(required = true, defaultValue = "2") + public String getMinorVersion() { return "2"; } + ... +} +---- + +Obviously, only changes made must be backward compatible, eg new members must be optional. + +Alternatively, you might also consider simply editing the source file, ie renaming the class and bumping up the value returned by `getMinorVersion()`. + +We also _don't_ recommend using inheritance (ie `ToDoItemV1_2` should not inherit from `ToDoItemV1_1`; this creates unnecessary complexity downstream if generating XSDs and DTOs for the downstream consumer. + +