Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: use Data Container as declarative DataManager #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mariodavid
Copy link
Contributor

@mariodavid mariodavid commented Sep 5, 2020

Idea: Declarative DataManager Loading

In this branch the default treatment room in the UI is not loaded via dataManager.load(DefaultTreatmentRoom.class).query("...").optional().

Instead, the dataCotainer concept is utilized to define the loading of the optional instance
of the DefaultTreatmentRoom in a declarative XML based manner. This way, the java code is simplified in the controller. It only requires to define the parameter as well as pick up the value via defaultTreatmentRoomDc.getItem().

The Reason: higher level abstraction

The reason behind it is the same which led to the introduction of the various screen facets like the MessageDialogFacet. It pushes the complexity of "how to load a particular entity" into the declarative XML part. The Java code then can just rely on the defaultTreatmentRoomDc to correctly fetched the data. This simplifies the java code and provides a higher level abstraction in the UI controller to work with.

Example: Default Treatment Room Lookup

The declarative definition of the instance container looks like this:

<instance id="defaultTreatmentRoomDc"
      class="com.alabastia.petclinic.entity.DefaultTreatmentRoom">
  <view extends="_local">
    <property name="treatmentRoom" view="_minimal" />
  </view>
  <loader id="defaultTreatmentRoomLc">
    <query>
      <![CDATA[select e from alabastiapetclinic_DefaultTreatmentRoom e where e.user = :currentUser]]>
    </query>
  </loader>
</instance>

The controller code is simplified as described above:

public class AlabastiaVisitEdit extends VisitEdit {
    @Inject
    protected InstanceContainer<DefaultTreatmentRoom> defaultTreatmentRoomDc;
    @Inject
    protected InstanceLoader<DefaultTreatmentRoom> defaultTreatmentRoomLc;

    @Subscribe
    protected void onInit(InitEvent event) {
        // provide the declarative dataContainer with required parameter
        defaultTreatmentRoomLc.setParameter("currentUser", currentUser());
    }
    
    @Subscribe
    protected void onAfterShow(AfterShowEvent event) {

        if (entityStates.isNew(getEditedEntity())) {

            getEditedEntity().setAssignedNurse(
                currentUser()
            );

            // retrieve a possible configured default treatment room through declarative dataContainer
            final TreatmentRoom defaultTreatmentRoom =
                defaultTreatmentRoomDc.getItem().getTreatmentRoom();

            initTreatmentRoom(defaultTreatmentRoom);
        }
    }
}

Current Problems with the declarative approach

Currently it works well for the happy path: the instanceContainer actually returns a value. The problem occurs when there is no result. For a regular instanceContainer this is not very often the case, because
normally an instance container is not used together with an associated data loader and a query.

But in this situation, the instance not being present by the query might be a valid business situation.

Currently, the InstanceLoaderImpl throws an exception in case the entity is not found:

entity = getDataManager().load(loadContext);

if (entity == null) {
    throw new EntityAccessException(container.getEntityMetaClass(), entityId);
}

Therefore, it requires in the controller to catch this exception as well as explicitly trigger the dataLoader.load() in order to programmatically catch the exception.

Proposed Solution

In order to make the usage of dataContainer as a declarative way of using the dataManager for this scenario work, it should be possible to mark an instance container as optional. This way when the instanceContainer is loaded and no result is found getItem() would either return null or alternatively an instance of Optional<DefaultTreatmentRoom>.

The resulting XML:

<instance id="defaultTreatmentRoomDc"
      optional="true"
      class="com.alabastia.petclinic.entity.DefaultTreatmentRoom">
  <view extends="_local">
    <property name="treatmentRoom" view="_minimal" />
  </view>
      <loader id="defaultTreatmentRoomLc">
        <query>
          <![CDATA[select e from alabastiapetclinic_DefaultTreatmentRoom e where e.user = :currentUser]]>
        </query>
      </loader>
</instance>

The corresponding controller code:

public class AlabastiaVisitEdit extends VisitEdit {
    @Inject
    protected InstanceContainer<DefaultTreatmentRoom> defaultTreatmentRoomDc;
    @Inject
    protected InstanceLoader<DefaultTreatmentRoom> defaultTreatmentRoomLc;

    @Subscribe
    protected void onInit(InitEvent event) {
        // provide the declarative dataContainer with required parameter
        defaultTreatmentRoomLc.setParameter("currentUser", currentUser());
    }
    
    @Subscribe
    protected void onAfterShow(AfterShowEvent event) {

        if (entityStates.isNew(getEditedEntity())) {

            getEditedEntity().setAssignedNurse(
                currentUser()
            );

            // retrieve a possible configured default treatment room through declarative dataContainer
            if (defaultTreatmentRoomDc.getItem() != null) {
               final TreatmentRoom defaultTreatmentRoom =
                               defaultTreatmentRoomDc.getItem().getTreatmentRoom();
               
               initTreatmentRoom(defaultTreatmentRoom); 
            }
        }
    }
}

With this non-exception-when-null behavior it would also not require to explicitly trigger the dataLoader load anymore and the @LoadDataBeforeShow

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants