= Meta-annotations

: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.
:page-partial:


Apache Isis supports the use of meta-annotations, as does Spring Boot and the ORM (xref:pjdo:ROOT:about.adoc[JDO/DataNucleus] from DN 5.x, xref:pjpa:ROOT:about.adoc[JPA/EclipseLink] from Eclipse 2.7 supporting JPA 2.2).

This allows you to define custom annotations that are more descriptive of your domain model, and meta-annotate your custom annotations with those of Apache Isis, Spring or the ORM.
In your domain model, you can just use the custom annotation.

The most common use case is to define meta-annotations for scalar types that are used in properties or parameters.

The xref:docs:starters:simpleapp.adoc[SimpleApp] starter app illustrates the usage with its `@Name` meta-annotation:

[source,java]
.Name.java
----
@Column(length = Name.MAX_LEN, allowsNull = "false")                // <.>
@Property(mustSatisfy = Name.Specification.class,                   // <.>
          maxLength = Name.MAX_LEN)
@Parameter(mustSatisfy = Name.Specification.class,                  // <.>
           maxLength = Name.MAX_LEN)
@ParameterLayout(named = "Name")                                    // <.>
@Target({ ElementType.METHOD, ElementType.FIELD,                    // <.>
          ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)                                 // <.>
public @interface Name {

    int MAX_LEN = 40;

    // ...

    class Specification extends AbstractSpecification2<String> {
        // ...
    }
}
----
<.> ORM annotation
<.> Annotations that are inferred if annotated on a property. +
The xref:refguide:applib:index/annotation/Property.adoc#mustSatisfy[@Property#mustSatisfy()] allows arbitrary validation rules to be specified for the property.

<.> Annotations that are inferred if annotated on a parameter +
The xref:refguide:applib:index/annotation/Parameter.adoc#mustSatisfy[@Parameter#mustSatisfy()] allows the same validation rules to be specified for the parameter.

<.> Layout annotations that are inferred if annotated on a parameter

<.> The locations that the meta annotation can be applied

<.> Required boilerplate to ensure that the meta annotation is part of the bytecode introspected at runtime

This annotation is used in the `SimpleObject` entity, for the "name" property and the "name" parameter of the "updateName()" action:

[source,java]
.SimpleObject.java
----
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.ToString.Include;

@ToString(onlyExplicitlyIncluded = true)
public class SimpleObject implements Comparable<SimpleObject> {

    // ...

    @Name
    @Getter @Setter @ToString.Include
    private String name;

    // ...
    public SimpleObject updateName(
            @Name final String name) {
        setName(name);
        return this;
    }
    // ...
}
----


[#properties-vs-parameters]
== Properties vs Parameters

Since the ORM handles persistence, its annotations can only be specified for properties, not for action parameters.
Using meta-annotations is a good way to unify the annotations that are required for properties vs parameters.

The table below summarises the equivalence of some of the most common cases.

.Comparing annotations of Properties vs Action Parameters
[cols="2,3,3",options="header"]
|===
|value type/semantic
|(JDO) property +
`javax.jdo.annotations`
|action parameter

|string (length)
|`@Column(length=50)`
|`@Parameter(maxLength=50)`

|big decimal (precision)
|`@Column(scale=2)`
|`@javax.validation.constraints.Digits(fraction=2)`

|optionality
|`@Column(allowsNull="true")`
|`@javax.annotation.Nullable` or `ParameterLayout(optionality=OPTIONAL`)
|===

