Migration Notes

to 2.0.0

Maven groupIds and artifactIds

These follow the format:

filename.xml
<groupId>org.apache.isis.group</groupId>
<artifactId>isis-group-artifact</artifactId>

where group is one of:

  • core

    Core framework, includes applib

  • viewers

    Currently wicket and restfulobjects.

  • persistence

    Currently only jdo is supported.

    it’s also possible to run the framework without any configured persistence, in other words to use view models and "roll-your-own" persistence as required.
  • security

    Currently bypass (in other words, a no-op), shiro and keycloak.

    Shiro also has its own plugin architecture of Realms. Apache Isis provides two Realm implementations (in extensions, below).

  • extensions

  • subdomains

  • mappings

  • valuetypes

  • incubator

isis.properties replaced by application.properties

Apache Isis v1.x used isis.properties for configuration properties. Since in v2 the framework is bootstrapped and configured using Spring Boot, we decided to use the Spring Boot conventions for the configuration files.

Accordingly, configuration properties should now be stored either in application.properties and/or application.yml. These are searched for in multiple locations (see Spring docs) and can be overridden using either system properties or environment variables.

The starter apps use these two locations:

  • ./application.yml

    in the default package (ie directly in src/main/resources) for static configuration that doesn’t change between environments

  • config/application.properties

in the config package (ie in src/main/resources/config) for dynamic configuration that changes between environments, for example JDBC URLs.

We’ve found that this schema works well with IDEs such as IntelliJ that provide auto-complete of configuration properties.

Configuration property names

These now follow, more or less, the groupIds. Very approximately:

Table 1. Configuration property changes
Key prefix was (in v1.x) Key prefix is now (in v2.x)

isis.xxx

isis.applib.xxx or
isis.core.xxx

isis.authentication

isis.security

isis.persistor

isis.persistence

isis.persistor.datanucleus

isis.persistence.jdo-datanucleus

isis.persistor.datanucleus.impl

isis.persistence.jdo-datanucleus.impl

isis.viewers

isis.metamodel.ui.

isis.services.xxx

isis.core.runtime.services.xxx

Note also Spring Boot allows configuration properties to be specified using either camelCase or kebab-case. In general it’s fine to use either, with a preference for kebab-case.

The only exception is the configuration properties that reside under isis.persistence.jdo-datanucleus.impl (what in Isis v1.x was the isis.persistor.datanucleus.impl key); this prefix is stripped off and all remaining properties passed through unchanged to DataNucleus. These must be kept in camel case format because DataNucleus doesn’t recognise kebab case.

For example, use isis.persistence.jdo-datanucleus.impl.javax.jdo.option.ConnectionURL (and not connection-url for the last part of the key).

Here, in more detail, are the before and after for recognised configuration properties.

Table 2. Configuration property changes
Key was (in v1.x) Key is now (in v2.x)

isis.authentication.shiro.
  autoLogoutIfAlreadyAuthenticated

isis.security.shiro.
  auto-logout-if-already-authenticated

isis.locale

isis.core.runtime.locale

isis.objects.editing

isis.applib.metamodel.
  domain-object.editing

isis.persistor.datanucleus.
  classMetadataLoadedListener

isis.persistence.jdo-datanucleus.
  class-metadata-loaded-listener

isis.persistor.datanucleus.installFixtures

(removed)

isis.persistor.datanucleus.impl.
  datanucleus.schema.autoCreateAll

isis.persistence.jdo-datanucleus.impl.
  datanucleus.schema.autoCreateAll

isis.persistor.datanucleus.impl.
  datanucleus.schema.validateAll

isis.persistence.jdo-datanucleus.impl.
  datanucleus.schema.validateAll

isis.persistor.datanucleus.impl.
  datanucleus.schema.validateTables

isis.persistence.jdo-datanucleus.impl.
  datanucleus.schema.validateTables

isis.persistor.datanucleus.impl.
  datanucleus.schema.validateConstraints

isis.persistence.jdo-datanucleus.impl.
  datanucleus.schema.validateConstraints

isis.persistor.datanucleus.impl.
  datanucleus.persistenceByReachabilityAtCommit

isis.persistence.jdo-datanucleus.impl.
  datanucleus.persistenceByReachabilityAtCommit

isis.persistor.datanucleus.impl.
  datanucleus.identifier.case

isis.persistence.jdo-datanucleus.impl.
  datanucleus.identifier.case

isis.persistor.datanucleus.impl.
  datanucleus.cache.level2.mode

isis.persistence.jdo-datanucleus.impl.
  datanucleus.cache.level2.mode

isis.persistor.datanucleus.impl.
  javax.jdo.option.ConnectionDriverName

isis.persistence.jdo-datanucleus.impl.
  javax.jdo.option.ConnectionDriverName

isis.persistor.datanucleus.impl.
  javax.jdo.option.ConnectionURL

isis.persistence.jdo-datanucleus.impl.
  javax.jdo.option.ConnectionURL

isis.persistor.datanucleus.impl.
  javax.jdo.option.ConnectionUserName

isis.persistence.jdo-datanucleus.impl.
  javax.jdo.option.ConnectionUserName

isis.persistor.datanucleus.impl.
  javax.jdo.option.ConnectionPassword

isis.persistence.jdo-datanucleus.impl.
  javax.jdo.option.ConnectionPassword

isis.persistor.datanucleus.impl.
  javax.jdo.option.
    PersistenceManagerFactoryClass

isis.persistence.jdo-datanucleus.impl.
  javax.jdo.option.
    PersistenceManagerFactoryClass

isis.persistor.datanucleus.
   standalone-collection.bulk-load

(removed)

isis.persistor.enforceSafeSemantics

(removed)

isis.reflector.explicitAnnotations.
  action

isis.applib.annotation.action
  .explicit

isis.reflector.facet.
  actionAnnotation.
    domainEvent.postForDefault

isis.applib.annotation.
  action.
    domain-event.post-for-default

isis.reflector.facet.
  collectionAnnotation.
    domainEvent.postForDefault

isis.applib.annotation.
  collection.
    domain-event.post-for-default

isis.reflector.facet.
  cssClass.patterns

isis.applib.annotation.
  action-layout.
    css-class.patterns

isis.reflector.facet.
  domainObjectAnnotation.
    createdLifecycleEvent.postForDefault

isis.applib.annotation.
  domain-object.
    created-lifecycle-event.post-for-default

isis.reflector.facet.
  domainObjectAnnotation.
    loadedLifecycleEvent.postForDefault

isis.applib.annotation.
  domain-object.
    loaded-lifecycle-event.post-for-default

isis.reflector.facet.
  domainObjectAnnotation.
    persistingLifecycleEvent.postForDefault

isis.applib.annotation.
  domain-object.
    persisting-lifecycle-event.post-for-default

isis.reflector.facet.
  domainObjectAnnotation.
    persistedLifecycleEvent.postForDefault

isis.applib.annotation.
  domain-object.
    persisted-lifecycle-event.post-for-default

isis.reflector.facet.
  domainObjectAnnotation.
    removingLifecycleEvent.postForDefault

isis.applib.annotation.
  domain-object.
    removing-lifecycle-event.post-for-default

isis.reflector.facet.
  domainObjectAnnotation.
    updatedLifecycleEvent.postForDefault

isis.applib.annotation.
  domain-object.
    updated-lifecycle-event.post-for-default

isis.reflector.facet.
  domainObjectAnnotation.
    updatingLifecycleEvent.postForDefault

isis.applib.annotation.
  domain-object.
    updating-lifecycle-event.post-for-default

isis.reflector.facet.
  domainObjectLayoutAnnotation.
    cssClassUiEvent.postForDefault

isis.applib.annotation.
  domain-object-layout.
    css-class-ui-event.post-for-default

isis.reflector.facet.
  domainObjectLayoutAnnotation.
    iconUiEvent.postForDefault

isis.applib.annotation.
  domain-object-layout.
    icon-ui-event.post-for-default

isis.reflector.facet.
  domainObjectLayoutAnnotation.
    layoutUiEvent.postForDefault

isis.applib.annotation.
  domain-object-layout.
    layout-ui-event.post-for-default

isis.reflector.facet.
  domainObjectLayoutAnnotation.
    titleUiEvent.postForDefault

isis.applib.annotation.
  domain-object-layout.
    title-ui-event.post-for-default

isis.reflector.facet.
  propertyAnnotation.
    domainEvent.postForDefault

isis.applib.annotation.
  property.
    domain-event.post-for-default

isis.reflector.facet.
  viewModelLayoutAnnotation.
    cssClassUiEvent.postForDefault

isis.applib.annotation.
  view-model-layout.
    css-class-ui-event.post-for-default

isis.reflector.facet.
  viewModelLayoutAnnotation.
    iconUiEvent.postForDefault

isis.applib.annotation.
  view-model-layout.
    icon-ui-event.post-for-default

isis.reflector.facet.
  viewModelLayoutAnnotation.
    layoutUiEvent.postForDefault

isis.applib.annotation.
  view-model-layout.
    layout-ui-event.post-for-default

isis.reflector.facet.
  viewModelLayoutAnnotation.
    titleUiEvent.postForDefault

isis.applib.annotation.
  view-model-layout.
    title-ui-event.post-for-default

isis.reflector.facets.
  viewModelSemanticCheckingFacetFactory.
    enable

isis.applib.annotation.
  view-model.validation.
    semantic-checking.enable

isis.reflector.facet.
  filterVisibility

isis.core.metamodel.
  filter-visibility

isis.reflector.facets.
  ignoreDeprecated

isis.core.metamodel.programming-model.
  ignore-deprecated

isis.reflector.introspector.
  parallelize

isis.core.metamodel.introspector.
  parallelize

isis.reflector.introspector.
  mode

isis.core.metamodel.introspector.
  mode

isis.reflector.introspect.
  lockAfterFullIntrospection

isis.core.metamodel.introspector.
  lock-after-full-introspection

isis.reflector.introspect.
  validateIncrementally

isis.core.metamodel.introspector.
  validate-incrementally

isis.reflector.validator.
  parallelize

isis.core.metamodel.validator.
  parallelize

isis.reflector.validator.
  allowDeprecated

isis.core.metamodel.validator.
  allow-deprecated

isis.reflector.validator.
  ensureUniqueObjectTypes

isis.core.metamodel.validator.
  ensure-unique-object-types

isis.reflector.validator.
  checkModuleExtent

isis.core.metamodel.validator.
  check-module-extent

isis.reflector.validator.
  noParamsOnly

isis.core.metamodel.validator.
  no-params-only

isis.reflector.validator.
  actionCollectionParameterChoices

isis.core.metamodel.validator.
  action-collection-parameter-choices

isis.reflector.validator.
  serviceActionsOnly

isis.core.metamodel.validator.
  service-actions-only

isis.reflector.validator.mixinsOnly

isis.core.metamodel.validator.
  mixins-only

isis.reflector.validator.explicitObjectType

isis.core.metamodel.validator.
  explicit-object-type

isis.reflector.validator.
  jaxbViewModelNotAbstract

isis.core.metamodel.validator.
  jaxb-view-model.not-abstract

isis.reflector.validator.
  jaxbViewModelNotInnerClass

isis.core.metamodel.validator.
  jaxb-view-model.not-inner-class

isis.reflector.validator.
  jaxbViewModelNoArgConstructor

isis.core.metamodel.validator.
  jaxb-view-model.no-arg-constructor

isis.reflector.validator.
  jaxbViewModelReferenceTypeAdapter

isis.core.metamodel.validator.
  jaxb-view-model.reference-type-adapter

isis.reflector.validator.
  jaxbViewModelDateTimeTypeAdapter

isis.core.metamodel.validator.
  jaxb-view-model.date-time-type-adapter

isis.reflector.validator.
  jdoqlFromClause

isis.core.metamodel.validator.
  jdoql.from-clause

isis.reflector.validator.
  jdoqlVariablesClause

isis.core.metamodel.validator.
  jdoql.variables-clause

isis.service.
  email.port

isis.core.runtime-services.
  email.port

isis.service.
  email.socketConnectionTimeout

isis.core.runtime-services.
  email.socket-connection-timeout

isis.service.
  email.socketTimeout

isis.core.runtime-services.
  email.socket-timeout

isis.service.
  email.throwExceptionOnFail

isis.core.runtime-services.
  email.throw-exception-on-fail

isis.service.
  email.override.to

isis.core.runtime-services.
  email.override.to

isis.service.
  email.override.cc

isis.core.runtime-services.
  email.override.cc

isis.service.
  email.override.bcc

isis.core.runtime-services.
  email.override.bcc

isis.service.
  email.sender.hostname

isis.core.runtime-services.
  email.sender.hostname

isis.service.
  email.sender.username

isis.core.runtime-services.
  email.sender.username

isis.service.
  email.sender.password

isis.core.runtime-services.
  email.sender.password

isis.service.
  email.sender.address

isis.core.runtime-services.
  email.sender.address

isis.service.
  email.tls.enabled

isis.core.runtime-services.
  email.tls.enabled

isis.services.
  applicationFeatures.init

isis.core.runtime-services.
  application-features.init

isis.services.audit.
  objects

isis.applib.annotation.
  domain-object.auditing

isis.services.command.
  actions

isis.applib.annotation.
  action.command

isis.services.command.
  properties

isis.applib.annotation.
  property.command

isis.services.
  container.disableAutoFlush

isis.core.runtime-services.
  repository-service.disable-auto-flush

isis.services.
  ExceptionRecognizerCompositeForJdoObjectStore.
    disable

isis.core.runtime-services.
  exception-recognizer.jdo.
    disable

isis.services.injector.setPrefix

(removed)

isis.services.injector.injectPrefix

(removed)

isis.services.publishing.
  actions

isis.applib.annotation.
  action.publishing

isis.services.publishing.
  objects

isis.applib.annotation.
  domain-object.publishing

isis.services.publishing.
  properties

isis.applib.annotation.
  property.publishing

isis.services.
  translation.po.mode

isis.core.runtime-services.
  translation.po.mode

isis.viewer.
  wicket.breadcrumbs.showChooser

isis.viewer.
  wicket.bookmarks.show-drop-down-on-footer

isis.viewer.
  wicket.whereAmI.enabled

isis.viewer.
  wicket.breadcrumbs.enabled

isis.viewer.
  wicket.whereAmI.maxParentChainLength

isis.viewer.
  wicket.breadcrumbs.max-parent-chain-length

isis.viewers.
  collectionLayout.defaultView

isis.applib.annotation.
  collection-layout.default-view

isis.viewers.
  .paged.parented

isis.applib.annotation.
  collection-layout.paged

isis.viewers.
  .paged.standAlone

isis.applib.annotation.
  domain-object-layout.paged

isis.viewers.
  .parameterLayout.labelPosition

isis.applib.annotation.
  parameter-layout.label-position

isis.viewers.
  .parameterLayout.label

(removed)

isis.viewers.
  .propertyLayout.labelPosition

isis.applib.annotation.
  property-layout.label-position

isis.viewers.
  .propertyLayout.label

(removed)

isis.timezone

isis.core.runtime.timezone

isis.value.format["int"]

isis.value-types.
  java-math.big-integer.format

isis.value-types.
  primitives.int.format

isis.value.format["decimal"]

isis.value-types.
  java-math.big-decimal.format

isis.value.format["byte"]

isis.value-types.
  java-lang.byte.format

isis.value.format["double"]

isis.value-types.
  java-lang.double.format

isis.value.format["float"]

isis.value-types.
  java-lang.float.format

isis.value.format["long"]

isis.value-types.
  java-lang.long.format

isis.value.format["short"]

isis.value-types.
  java-lang.short.format

isis.value.format["percentage"]

isis.legacy.value-types.
  percentage.format

isis.value.format["datetime"]

isis.value-types.
  joda.local-date-time.format

isis.value-types.
  java-time.local-date-time.format

isis.value-types.
  java-time.offset-date-time.format

isis.value-types.
  java-util.date.format

isis.value.format["date"]

isis.value-types.
  java-sql.date.format

isis.value-types.
  joda.date-time.format

isis.value-types.
  joda.local-date.format

isis.value.format["time"]

isis.value-types.
  java-sql.time.format

isis.value-types.
  java-time.offset-time.format

isis.value-types.
  java-time.local-time.format

isis.value-types.
  zoned-date-time.local-time.format

isis.value.format["timestamp"]

isis.value-types.
  java-sql.timestamp.format

isis.value.money.current

isis.legacy.value-types.money.current

| - isis.value.format → isis.metamodel.ui.value.format - isis.value.money → isis.valuetypes.money - isis.service.xxx → isis.core.services.email - isis.services.container → isis.core.services.repository-service

Annotations

Collections are no longer editable. The @Collection#editing() and @Collection#editingDisabledReason() members have been removed.

No longer any archetypes

The archetypes have been replaced by starter apps. This has several benefits:

  • for users of the framework:

    • github repos are now better understood than Maven archetypes

    • it is easier to provide improvements/patches as pull requests

    • a forked repo is immediately under code control

  • for committers:

    • contributions can be merged in more easiler

    • it simplifies the release process

2.0.0-M2 to 2.0.0-M3

  • o.a.isis.schema.utils.Xxx in the applib have moved to o.a.isis.applib.util.schema

  • o.a.isis.schema.utils.jaxbadapters.Xxx in the applib have moved to o.a.isis.applib.jaxbadapters

  • BackgroundService replaced by the WrapperFactory#async(Object)

Server-Sent-Event (SSE) Support (ISIS-2102)

Experimental feature to allow for submission of background-tasks, that themselves may fire UI-events to update eg. a progress-bar.

To make this work we introduce following components:

  • A SSE Servlet listening on '/sse' for client requests.

  • Client-Side JavaScript that can subscribe 'EventStream’s to the SSE Servlet

  • An EventStreamSource interface for any designated background task to implement.

  • An EventStreamService, that allows for such EventStreamSource objects to be submitted for execution on a thread-pool.

  • An EventStreamSource is associated with an EventStream on which it may fire update events.

  • These update events are propagated to the SSE Servlet, which informs its listening clients with the update event’s payload data.

A first prototypical implementation of this mechanism also introduces a programming model extension, which for now only works for 'Markup' properties.

The Subscribing ViewModel

@ViewModel
class View {
	@Property(observe = BackgroundTask.class) // <-- client-side subscription to events of this type
	Markup markup; // <-- on ui-event, the markup component is client-side updated by the EventStreamSource.getPayload()
}

The Background Task

class BackgroundTask implements EventStreamSource {

    int progress = 0;

	@Override
	public void run(EventStream eventStream) {
	    // do something time consuming and eventually fire an update
	    ...
	    ++progress;
	    eventStream.fire(this);
	    ...
	}

	@Override
	public Markup getPayload() {
	    return new Markup("my current progress is " + progress);
	}

}

The Background Task Submitter

@DomainService
class Submitter {
    @Inject EventStreamService eventStreamService;

	@Action
	public void startBackgroundTask() {

		eventStreamService.submit(new BackgroundTask());

	}
}

ServicesInjector/ServiceRegistry

The ServicesInjector service was removed, replaced by new interfaces ServiceInjector (note: singular) and ServiceRegistry.

BuilderScripts simplified

The number of required type-parameters for 'BuilderScripts' has been reduced:

@Accessors(chain = true)
public class SimpleObjectBuilder
extends BuilderScriptAbstract<SimpleObject> { // <= only 1 type param

    @Getter
    private SimpleObject object;

	...
}

@AllArgsConstructor
public enum SimpleObject_persona
implements PersonaWithBuilderScript<SimpleObjectBuilder> /* <= only 1 type param */ ... {

    FOO("Foo"),
    BAR("Bar"),

	...

    public SimpleObjectBuilder builder() {
        return new SimpleObjectBuilder().setName(name);
    }

    public static class PersistAll
    extends PersonaEnumPersistAll<SimpleObject_persona, SimpleObject> /* <= only 2 type params */ {
		...
    }
}

2.0.0-M1 to 2.0.0-M2

AppConfig (ISIS-2039)

AppConfig is a new interface that is located through a variety of mechanisms:

  • CDI if available, else

  • Java 7’s ServiceLoader mechanism (META-INF/services/org.apache.isis.config.AppConfig file to be present), else

  • fallback to reading (peeking into) isis.properties.

Its API is simply:

@FunctionalInterface
public interface AppConfig {

    IsisConfiguration isisConfiguration();

}

The expected idiom is for the application’s AppManifest to also implement this, eg:

@javax.ejb.Singleton                                                (1)
public class HelloWorldAppManifest extends AppManifestAbstract
            implements AppConfig {                                  (2)

    ...

    @Override
    public IsisConfiguration isisConfiguration () {
        return IsisConfiguration.buildFromAppManifest(this);
    }
}
1 only required if the AppConfig is to be picked up using CDI

So, we have the AppManifest instantiated by CDI etc, and then the IsisConfiguration is built in turn from the AppManifest. Once the IsisConfiguration is created, it is immutable. And, following the above idiom, the IsisConfiguration also makes the AppManifest available:

public interface IsisConfiguration {
    ...
    AppManifest getAppManifest();
    ...
}

Table Tree Viewer (ISIS-898)

also: ISIS-1943,ISIS-1944,ISIS-1947

Note: Currently does not implement a Table Tree View but just a Tree View.

public API is:

  • TreeAdapter (provides the parent/child relationship information between pojos to derive a tree-structure)

  • TreeNode

  • TreePath (represents a coordinate-system to navigate any tree-structure)

public interface TreeAdapter<T> {

    Optional<T> parentOf(T value); // parent tree-node (pojo) of given value tree-node

    int childCountOf(T value); // number of child tree-nodes of given value tree-node

    Stream<T> childrenOf(T value); // stream of child tree-nodes of given value tree-node

}

// creating a tree starting at a given tree-node, where MyTreeAdapter implements TreeAdapter<T>

T root = ... // the tree's root (a pojo)
TreeNode<T> tree = TreeNode.lazy(root, MyTreeAdapter.class); // creates a tree-node with given 'root' as the tree's root

// expand a certain tree-node by specifying it's coordinates (TreePath) within the tree-structure

tree.expand(TreePath.of(0)); // expand the root node
tree.expand(TreePath.of(0, 1)); // expand the second child of the root node

A full example is showcased in the isis-demo …​

Implementation of TreeAdapter

public class FileSystemTreeAdapter implements TreeAdapter<FileNode> {

	@Override
	public Optional<FileNode> parentOf(FileNode value) {
		if(value.getType()==FileNode.Type.FileSystemRoot) {
			return Optional.empty();
		}
		val parentFolderIfAny = value.asFile().getParentFile();
		if(parentFolderIfAny==null) {
			return Optional.empty(); // unexpected code reach, but just in case
		}
		return Optional.ofNullable(parentFolderIfAny)
				.map(FileNodeFactory::toFileNode);
	}

	@Override
	public int childCountOf(FileNode value) {
		return (int) streamChildFiles(value).count();
	}

	@Override
	public Stream<FileNode> childrenOf(FileNode value) {
		return streamChildFiles(value)
				.map(FileNodeFactory::toFileNode);
	}

	// -- HELPER
	private static Stream<File> streamChildFiles(FileNode value){
		val file = value.asFile();
		val childFiles = file.listFiles();
		if(childFiles==null) {
			return Stream.empty();
		}
		return Stream.of(childFiles)
				.filter(f->!f.isHidden());
	}
}

where FileNode doesn’t, actually, need to implement TreeNode, it’s just a regular view model:

@XmlRootElement(name="FileNode")
@DomainObject(nature=Nature.VIEW_MODEL)
@ToString
public class FileNode {

	public static enum Type {
		FileSystemRoot,
		Folder,
		File
	}

	@Getter @Setter protected String path;
	@Getter @Setter protected Type type;

	public String title() {
		if(path==null) {
			return null;
		}
		val file = asFile();
		return file.getName().length()!=0 ? file.getName() : file.toString();
	}

	public String iconName() {
		return type!=null ? type.name() : "";
	}

	// -- BREADCRUMB SUPPORT

	@PropertyLayout(navigable=Navigable.PARENT, hidden=Where.EVERYWHERE)
	public FileNode getParent() {
	    val parentFile = asFile().getParentFile();
	    return parentFile!=null ? FileNodeFactory.toFileNode(parentFile) : null;
	}

	// -- INIT

	void init(File file) {
		this.path = file.getAbsolutePath();
		if(file.isDirectory()) {
			type = isRoot(file) ? Type.FileSystemRoot : Type.Folder;
		} else {
			type = Type.File;
		}
	}

	// -- HELPER

	File asFile() {
		return new File(path);
	}

	private static boolean isRoot(File file) {
		return file.getParent()==null;
	}
}

And finally the ViewModel that provides the tree for rendering:

@ViewModel
public class TreeDemo extends DemoStub {

	/**
	 * @return the demo tree view model as a property
	 */
	public TreeNode<FileNode> getFileSystemTree() {
    		val root = FileNodeFactory.defaultRoot();
    		val tree = TreeNode.lazy(root, FileSystemTreeAdapter.class);
    		tree.expand(TreePath.of(0)); // expand the root node
    		return tree;
	}

    }

}

isis-core-wrapper removed (ISIS-1838/1839)

guice removed from applib and core (ISIS-1892)

web.xml now much simpler (ISIS-1895)

see 'Servlet Context' below

@MemberGroupLayout was removed

Axon Eventbus Plugin

switching from axon 2.x to 3.x which involves that axon’s EventHandler annotation has moved: org.axonframework.eventhandling.annotation.EventHandler → org.axonframework.eventhandling.EventHandler

API Changes

  • IsisMatchers is no longer part of the 'core' API, but still available within test-scope.

  • Ensure as part of the 'core' API now accepts Java Predicates instead of hamcrest Matchers

  • deployment types SERVER_EXPLORATION, UNIT_TESTING have been removed

Environment

Some ways of setting the DeploymentType (using web.xml or WebServer cmd-line flags -t or --type) have been removed. Instead running in PROTOTYPING (exemplified with Jetty) can be done in following ways:

 export PROTOTYPING=true ; mvn jetty:run
 mvn -DPROTOTYPING=true jetty:run
 mvn -Disis.deploymentType=PROTOTYPING jetty:run

We also introduced a SPI to customize this behavior. This issue is tracked by https://issues.apache.org/jira/browse/ISIS-1991

Servlet Context

  • web.xml: no longer required to install listeners, filters and servlets; but is still required to configure the welcome page; org.apache.isis.core.webapp.IsisWebAppContextListener acts as the single application entry-point to setup the dynamic part of the ServletContext.

    • ResourceCachingFilter is now configured via annotations (Servlet 3.0 spec), no longer needed to be declared in web.xml

    • ResourceServlet is now configured via annotations (Servlet 3.0 spec), no longer needed to be declared in web.xml

    • IsisTransactionFilterForRestfulObjects is now configured via annotations (Servlet 3.0 spec), no longer needed to be declared in web.xml

    • webjars Servlet was removed, no longer needed to be declared in web.xml

    • Shiro Environment, no longer needs to be declared in web.xml

    • Wicket Environment, no longer needs to be declared in web.xml

    • RestEasy Environment, no longer needs to be declared in web.xml

    • IsisSessionFilter is now part of the RestEasy WebModule, no longer needs to be declared in web.xml

    • LogOnExceptionLogger, no longer needs to be declared in web.xml

  • web.xml apart from the new WebContextListener we introduce new web-specific (optional) config values, nothing else needs to configured here:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	id="WebApp_ID" version="3.1">
	<display-name>My App</display-name>

	<welcome-file-list>
		<welcome-file>about/index.html</welcome-file>
	</welcome-file-list>

	<!-- unique bootstrapping entry-point for web-applications -->
        <listener>
		<listener-class>org.apache.isis.core.webapp.IsisWebAppContextListener</listener-class>
	</listener>

	<!-- optional for overriding default 'wicket' -->
	<context-param>
		<param-name>isis.viewer.wicket.basePath</param-name>
		<param-value>my-wicket</param-value>
	</context-param>

	<!-- optional for overriding default 'org.apache.isis.viewer.wicket.viewer.IsisWicketApplication' -->
	<context-param>
		<param-name>isis.viewer.wicket.app</param-name>
		<param-value>domainapp.webapp.MyDomainApplication</param-value>
	</context-param>

	<!-- optional for overriding default 'restful' -->
	<context-param>
		<param-name>isis.viewer.restfulobjects.basePath</param-name>
		<param-value>my-restful</param-value>
	</context-param>

</web-app>

Module Shiro

module 'shiro' moved from /core to /core/plugins and its maven artifactId changed, to be in line with the other core-plugins:

<dependency>
	<groupId>org.apache.isis.core</groupId>
	<artifactId>isis-core-plugins-security-shiro</artifactId>
</dependency>

ObjectAdapter

ObjectAdapter is no longer holding a reference to an ObjectSpecification for the element type of collections. ObjectAdapter#getElementSpecification() moved to ObjectSpecification#getElementSpecification().

JAXB XmlAdapters (ISIS-1972)

We do now provide JAXB XmlAdapters for Java built-in temporal types in 'applib': org.apache.isis.applib.adapters.JaxbAdapters

Wicket-Viewer

Instead of browser built-in tooltip rendering, the framework now provides tooltips using JavaScript and CSS, currently with following stylesheet defaults:

.ui-tooltip {
    max-width: 300px;
    color: rgb(70, 69, 69);
    background-color: WhiteSmoke;
    text-align: center;
    padding: 5px 10px;
    border-radius: 4px;
    font-size: 12px;
    box-shadow: 0 0 7px black;

    position: absolute;
    z-index: 9999;
}

span.isis-component-with-tooltip,
label.isis-component-with-tooltip,
.isis-component-with-tooltip label,
strong.isis-component-with-tooltip  {
   text-decoration: underline dashed;
}

.ui-helper-hidden-accessible { display:none; } /* accessibility support disabled */

REST Viewer

The content negotiation parameter 'suppress' does now allow more control on which '$$..' properties one wants to suppress. New options are

public static enum SuppressionType {
    /** suppress '$$RO', RO Spec representation*/
    RO,

    /** suppress '$$href', hyperlink to the representation*/
    HREF,

    /** suppress '$$instanceId', instance id of the domain object*/
    ID,

    /** suppress '$$domainType', object spec of the domain object */
    DOMAIN_TYPE,

    /** suppress '$$title', title of the domain object*/
    TITLE,

    /** suppress all '$$...' entries*/
    ALL
}

where these are case-insensitive and may be combined to a comma-separated set. Eg. to suppress title and href one could simply request

application/json;profile=urn:org.apache.isis/v2;suppress=title,href

We do not break the previous behavior with 'suppress=true' being equivalent to 'suppress=ro'

new RestfulClient

Adds a new JAX-RS 2.0 compliant RestfulClient to core-applib:

Client-Side Setup:

<dependency>
	<groupId>org.apache.isis.core</groupId>
	<artifactId>isis-core-applib</artifactId>
	<version>2.0.0-M2-SNAPSHOT</version>
</dependency>
<dependency>
	<groupId>javax.ws.rs</groupId>
	<artifactId>javax.ws.rs-api</artifactId>
	<version>2.1.1</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.core</groupId>
	<artifactId>jersey-client</artifactId>
	<version>2.25.1</version>
</dependency>
<dependency>
	<groupId>org.eclipse.persistence</groupId>
	<artifactId>org.eclipse.persistence.moxy</artifactId>
	<version>2.6.0</version>
</dependency>

Synchronous example with Basic-Auth:

RestfulClientConfig clientConfig = new RestfulClientConfig();
clientConfig.setRestfulBase("http://localhost:8080/helloworld/restful/");
// setup basic-auth
clientConfig.setUseBasicAuth(true);
clientConfig.setRestfulAuthUser("sven");
clientConfig.setRestfulAuthPassword("pass");

RestfulClient client = RestfulClient.ofConfig(clientConfig);

Builder request = client.request(
				"services/myService/actions/lookupMyObjectById/invoke",
				SuppressionType.setOf(SuppressionType.RO));

Entity<String> args = client.arguments()
		.addActionParameter("id", "12345")
		.build();

Response response = request.post(args);

ResponseDigest<MyObject> digest = client.digest(response, MyObject.class);

if(digest.isSuccess()) {
	System.out.println("result: "+ digest.get().get$$instanceId());
} else {
	digest.getFailureCause().printStackTrace();
}

Asynchronous example with Basic-Auth:

RestfulClientConfig clientConfig = new RestfulClientConfig();
clientConfig.setRestfulBase("http://localhost:8080/helloworld/restful/");
// setup basic-auth
clientConfig.setUseBasicAuth(true);
clientConfig.setRestfulAuthUser("sven");
clientConfig.setRestfulAuthPassword("pass");

RestfulClient client = RestfulClient.ofConfig(clientConfig);

Builder request = client.request(
                "services/myService/actions/lookupMyObjectById/invoke",
                SuppressionType.setOf(SuppressionType.RO));

Entity<String> args = client.arguments()
        .addActionParameter("id", "12345")
        .build();

Future<Response> asyncResponse = request
        .async()
        .post(args);

CompletableFuture<ResponseDigest<MyObject>> digestFuture =
                client.digest(asyncResponse, MyObject.class);

ResponseDigest<MyObject> digest = digestFuture.get(); // blocking

if(digest.isSuccess()) {
    System.out.println("result: "+ digest.get().get$$instanceId());
} else {
    digest.getFailureCause().printStackTrace();
}

Concurrent Computation

Support for concurrent computation within an open session utilizing a ForkJoinPool

Supplier<T> computation = ()->doSomeComputation();
CompletableFuture<T> completableFuture = IsisContext.compute(computation);

T result = completableFuture.get(); // blocking call

ConfigurationService

ConfigurationService and its internal implementation(s) were removed, instead use IsisConfiguration, which can be retrieved either via injection or static method:

@Inject IsisConfiguration configuration;
// or
IsisConfiguration configuration = IsisContext.getConfiguration();

Configuration Menu

The Configuration Menu within the UI now uses its own (and completely separated) interface, that handles masking of sensitive values (eg. passwords):

package org.apache.isis.applib.services.confview;

public interface ConfigurationViewService {
    /**
     * Returns all properties, each as an instance of {@link ConfigurationProperty} (a view model).
     * Mask sensitive values if required.
     */
    Set<ConfigurationProperty> allProperties();
}

@PostConstuct

@PostConstuct methods declared with domain objects no longer get passed over the IsisConfiguration. For now only zero-arg initializers are supported. (We might re-add parameter support, this is work in progress)

Wicket-Viewer

custom theme providers (ISIS-2047)

Customize the ThemeChooser by providing your own implementation of IsisWicketThemeSupport

public interface IsisWicketThemeSupport {
    ThemeProvider getThemeProvider();
    List<String> getEnabledThemeNames();
}

to be configured using

isis.viewer.wicket.themes.provider=org.my.IsisWicketThemeSupport

Removed o.a.i.WebServer (ISIS-2067)

might need to reinstate, or long-term will have Spring Boot etc do the bootstrapping.

1.x to 2.0.0-M1

java.time support (ISIS-1636)

The framework supports following temporal values from the Java Time API (and Joda):

Date only

  • java.sql.Date

  • java.time.LocalDate (since 2.0.0-M1)

  • org.joda.time.LocalDate

Date and Time

  • java.util.Date

  • java.sql.Timestamp

  • java.time.LocalDateTime (since 2.0.0-M1)

  • java.time.OffsetDateTime (since 2.0.0-M1)

  • org.joda.time.DateTime

  • org.joda.time.LocalDateTime

View Model Example

this example is now out of date; more recent releases have moved changed the name and package of some of these adapter classes.

If used with JAXB View Models, you need to specify specific XmlAdapters as provided by org.apache.isis.applib.jaxbadapters.JaxbAdapters.*. See this JAXB Viewmodel example using lombok:

@XmlRootElement(name = "Demo")
@XmlType
@XmlAccessorType(XmlAccessType.FIELD)
@DomainObject(nature=Nature.VIEW_MODEL)
public class TemporalDemo {

    // -- DATE ONLY (LOCAL TIME)

    @XmlElement @XmlJavaTypeAdapter(SqlDateAdapter.class)
    @Getter @Setter private java.sql.Date javaSqlDate;

    @XmlElement @XmlJavaTypeAdapter(LocalDateAdapter.class)
    @Getter @Setter private LocalDate javaLocalDate;

    // -- DATE AND TIME (LOCAL TIME)

    @XmlElement @XmlJavaTypeAdapter(DateAdapter.class)
    @Getter @Setter private Date javaUtilDate;

    @XmlElement @XmlJavaTypeAdapter(SqlTimestampAdapter.class)
    @Getter @Setter private java.sql.Timestamp javaSqlTimestamp;

    @XmlElement @XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
    @Getter @Setter private LocalDateTime javaLocalDateTime;

    // -- DATE AND TIME (WITH TIMEZONE OFFSET)

    @XmlElement @XmlJavaTypeAdapter(OffsetDateTimeAdapter.class)
    @Getter @Setter private OffsetDateTime javaOffsetDateTime;

    // --

    public void initDefaults() {

        log.info("TemporalDemo::initDefaults");

        javaUtilDate = new Date();
        javaSqlDate = new java.sql.Date(System.currentTimeMillis());
        javaSqlTimestamp = new java.sql.Timestamp(System.currentTimeMillis());

        javaLocalDate = LocalDate.now();
        javaLocalDateTime = LocalDateTime.now();
        javaOffsetDateTime = OffsetDateTime.now();
    }

}

Override hsqldb for integtests (ISIS-1958)

these examples are now out-of-date, superceded by later milestone releases.

sample usage:

public abstract class MyIntegTestAbstract extends IntegrationTestJupiter {

  public MyIntegTestAbstract() {
    super(new MyModule()
      .withAdditionalModules( /* ... */)
      ...
      .withConfigurationProperties(databaseConfigForTest())
    );
  }

  private static Map<String, String> databaseConfigForTest() {
    Map<String, String> map = new HashMap<>();

    // for refernce see AppManifest.Util.withJavaxJdoRunInMemoryProperties(map)

    map.put(AppManifest.Util.ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionURL", "jdbc:mariadb://127.0.0.1/demo");
    map.put(AppManifest.Util.ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionDriverName", "org.mariadb.jdbc.MariaDbDataSource");
    map.put(AppManifest.Util.ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionUserName", "sven");
    map.put(AppManifest.Util.ISIS_PERSISTOR_DATANUCLEUS_IMPL + "javax.jdo.option.ConnectionPassword", "pass");

    return map;
  }
  ...

}

Reworked o.a.i.WebServer (ISIS-1067)

Changed parameter flags:

  • -m, --manifest, --appManifest

  • -d, --dev, --prototype

  • -p, --port

Not other flags supported.