Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Scout/Concepts/Extensibility"

(Overview)
(Replaced content with "{{ScoutPage|cat=Concepts}} Moved to new Scout documentation at http://eclipsescout.github.io/6.0/technical-guide.html#texts")
Line 1: Line 1:
 
{{ScoutPage|cat=Concepts}}
 
{{ScoutPage|cat=Concepts}}
  
== Overview ==
+
Moved to new Scout documentation at http://eclipsescout.github.io/6.0/technical-guide.html#texts
{{important|Required version|The API described here requires version 4.2 or newer.}}
+
 
+
Since December 2014 and Scout 4.2 or newer a new extensibility concept is available for Scout. This article explains the new features and gives some examples how to use them.
+
 
+
When working with large business applications it is often required to split the application into several modules. Some of those modules may be very basic and can be reused in multiple applications. For those it makes sense to provide them as binary library. But what if you have created great {{ScoutLink|Concepts|Template}}s for
+
your applications but in one special case you want to include one more column in a table or want to execute some other code when a pre-defined context menu is pressed? You cannot just modify the code because it is a general library used everywhere. This is where the new extensibility concept helps.
+
 
+
To achieve this two new elements have been introduced:
+
* Extension Classes: Contains modifications for a target class. Modifications can be new elements or changed behavior of existing elements.
+
* Extension Registry: Service holding all Extensions that should be active in the application.
+
 
+
== Extensions - Changing behavior ==
+
 
+
Extensions contain modifications to a target class. This target class must be extensible. All elements that implement <code>org.eclipse.scout.rt.shared.extension.IExtensibleObject</code> are extensible.
+
And for all extensible elements there exists a corresponding abstract extension class.
+
 
+
Examples:
+
* <code>AbstractStringField</code> is extensible. Therefore there is a class <code>AbstractStringFieldExtension</code>.
+
* <code>AbstractCodeType</code> is extensible. Therefore there is a class <code>AbstractCodeTypeExtension</code>.
+
 
+
Target classes can be all that are <code>instanceof</code> those extensible elements.This means an <code>AbstractStringFieldExtension</code> can be applied to <code>AbstractStringField</code> and all child classes.
+
 
+
Extensions contain methods for all Scout Operations ({{ScoutLink|Concepts|Exec_Methods}}). Those methods have the same signature except that they have one more input parameter.
+
This method allows you to intercept the given Scout Operation and execute your own code even though the declaring class exists in a binary library. It is then your decision if you call the original code or completely replace it.
+
To achieve this the [http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern Chain Pattern] is used: All extensions for a target class are called as part of a chain. The order is given by the order in which the extensions are registered. And the original method of the Scout element is an extension as well.
+
 
+
[[Image:Scout.extensibility.chain.concept.png|600px]]
+
 
+
The following example changes the initial value of a {{ScoutLink|Concepts|StringField}} called <code>NameField</code>:
+
 
+
<source lang="java">
+
// extension to intercept e.g. execInitField()
+
public class NameFieldExtension extends AbstractStringFieldExtension<NameField> {
+
 
+
  public NameFieldExtension(NameField owner) {
+
    super(owner);
+
  }
+
 
+
  @Override
+
  public void execInitField(ExecInitChain chain) {
+
    chain.execInitField(); // call the original exec init. whatever it may do.
+
    getOwner().setValue("FirstName LastName");  // overwrite the initial value of the name field
+
  }
+
}
+
 
+
</source >
+
 
+
&nbsp;
+
 
+
The extension registration can be done in the <code>Activator</code>'s <code>start()</code> method of the plugin containing the extensions:
+
 
+
<source lang="java">
+
 
+
new Job("register extensions") {
+
  @Override
+
  protected IStatus run(IProgressMonitor monitor) {
+
    // register extension so that it becomes active
+
    SERVICES.getService(IExtensionRegistry.class).register(NameFieldExtension.class);
+
    return Status.OK_STATUS;
+
  }
+
}.schedule();
+
 
+
 
+
</source >
+
 
+
&nbsp;
+
 
+
== Contributions - Add new elements ==
+
 
+
The section before explained how to modify the behavior of existing Scout elements. This section will describe how to contribute new elements into existing containers.
+
 
+
This is done by using the same mechanism as before. It is required to create an Extension too. But instead of overwriting any Scout Operation we directly define the new elements within the Extension. A lot of new elements can be added this way: {{ScoutLink|Concepts|Field}}s, {{ScoutLink|Concepts|Menu}}s, {{ScoutLink|Concepts|Column}}s, {{ScoutLink|Concepts|Code}}s, ...
+
 
+
Some new elements may also require a new [http://en.wikipedia.org/wiki/Data_transfer_object DTO] ({{ScoutLink|Concepts|FormData}}, {{ScoutLink|Concepts|TablePageData}}, {{ScoutLink|Concepts|TableData}}) to be filled with data from the server. The corresponding DTO for the extension is automatically created when using the {{ScoutLink|SDK}} 4.2 or newer and having the <code>@Data</code> annotation specified on your extension. As soon as the DTO extension has been registered in the <code>IExtensionRegistry</code> service it is automatically created when the target DTO is created and will also be imported and exported automatically!
+
 
+
The following example adds two new fields for salary and birthday to a <code>PersonForm</code>. Please note the <code>@Data</code> annotation which describes where the DTO for this extension should be created.
+
 
+
<source lang="java">
+
// extension for the MainBox of the PersonForm
+
@Data(PersonFormMainBoxExtensionData.class)
+
public class PersonFormMainBoxExtension extends AbstractGroupBoxExtension<PersonForm.MainBox>{
+
 
+
  public PersonFormMainBoxExtension(MainBox ownerBox) {
+
    super(ownerBox);
+
  }
+
 
+
  @Order(2000)
+
  public class SalaryField extends AbstractBigDecimalField {
+
  }
+
 
+
  @Order(3000)
+
  public class BirthdayField extends AbstractDateField {
+
  }
+
}
+
 
+
// register the extension in the Job in your Activator class like in the chapter before
+
SERVICES.getService(IExtensionRegistry.class).register(PersonFormMainBoxExtension.class);
+
 
+
</source>
+
 
+
&nbsp;
+
 
+
Then the {{ScoutLink|SDK}} automatically creates the extension DTO which could look as follows. Please note: The DTO is generated automatically but you have to register the generated DTO manually!
+
 
+
<source lang="java">
+
 
+
// automatically generated by the Scout SDK
+
@Extends(PersonFormData.class)
+
public class PersonFormMainBoxExtensionData extends AbstractFormFieldData {
+
 
+
  public Salary getSalary() {
+
    return getFieldByClass(Salary.class);
+
  }
+
 
+
  public  getBirthday() {
+
    return getFieldByClass(Birthday.class);
+
  }
+
 
+
  public static class Salary extends AbstractValueFieldData<BigDecimal> {
+
  }
+
 
+
  public static class Birthday extends AbstractValueFieldData<Date> {
+
  }
+
}
+
 
+
// The extension data must be registered manually in the Job in your Activator class like in the chapter before!
+
SERVICES.getService(IExtensionRegistry.class).register(PersonFormMainBoxExtensionData.class);
+
 
+
</source>
+
 
+
&nbsp;
+
 
+
You can also access the values of the DTO extension as follows:
+
 
+
<source lang="java">
+
 
+
// create a normal FormData
+
// contributions are added/imported/exported automatically
+
PersonFormData data = new PersonFormData();
+
 
+
// access the data of an extension
+
PersonFormMainBoxExtensionData c = data.getContribution(PersonFormMainBoxExtensionData.class);
+
c.getSalary().setValue(new BigDecimal("200.0"));
+
 
+
</source>
+
 
+
&nbsp;
+
 
+
=== Extending a form and a handler ===
+
 
+
Extending a <code>AbstractForm</code> and one (or more) of its <code>AbstractFormHandler</code>s that can be achieved as follows:
+
<source lang="java">
+
public class DesktopFormExtension extends AbstractFormExtension<DesktopForm> {
+
 
+
  public DesktopFormExtension(DesktopForm ownerForm) {
+
    super(ownerForm);
+
  }
+
 
+
  @Override
+
  public void execInitForm(FormInitFormChain chain) throws ProcessingException {
+
    chain.execInitForm();
+
    // Example logic: Access the form, disable field
+
    getOwner().getTestField().setEnabled(false);
+
  }
+
 
+
  public void testMethod() {
+
    MessageBox.showOkMessage("Extension method test", "A method from the form extension was called", "");
+
  }
+
 
+
  public static class NewFormHandlerExtension extends AbstractFormHandlerExtension<DesktopForm.ViewHandler> {
+
 
+
    public NewFormHandlerExtension(ViewHandler owner) {
+
      super(owner);
+
    }
+
 
+
    @Override
+
    public void execPostLoad(FormHandlerPostLoadChain chain) throws ProcessingException {
+
      chain.execPostLoad();
+
      // Example logic: Show a message box after load
+
      MessageBox.showOkMessage("Extension test", "If you can read this, the extension works correctly", "");
+
      // Access element from the outer extension.
+
      DesktopFormExtension extension = ((AbstractForm) getOwner().getForm()).getExtension(DesktopFormExtension.class);
+
      extension.testMethod();
+
    }
+
 
+
  }
+
}
+
</source>
+
There are a few things to note about this example:
+
* It is only necessary to register the outer form extension, not the inner handler extension as well.
+
* The inner handler extension must be <code>static</code>, otherwise an Exception will occur when the extended form is being started!
+
* You can access the element you are extending by calling <code>getOwner()</code>.
+
* Since you cannot access elements from your form extension directly from the inner handler extension (because it is static), you will need to retrieve the form extension via the <code>getExtension(Class<T extends IExtension<?>>)</code> method on the extended object, as done here to retrieve the form extension from the form handler extension.
+
 
+
&nbsp;
+
 
+
== Move elements ==
+
 
+
You can also move existing Scout elements to other positions. For this you have to register a move command in the <code>IExtensionRegistry</code>. As with all extension registration it is added to the extension registration Job in your <code>Activator</code> class:
+
 
+
<source lang="java">
+
 
+
// the name field is moved into the LastBox at position 20.
+
SERVICES.getService(IExtensionRegistry.class).registerMove(NameField.class, LastBox.class, 20d);
+
 
+
</source>
+
 
+
&nbsp;
+
 
+
== Migration ==
+
 
+
The new extensibility concept is added on top of all existing extension possibilities like injection or sub-classing. Therefore it works together with the current mechanisms. But for some use cases (like modifying template classes) it offers a lot of benefits. Therefore no migration is necessary. The concepts do exist alongside each others.
+
 
+
However there is one impact: Because the Scout Operation methods are now part of a call chain they may no longer be invoked directly. So any call to e.g. <code>execValidateValue()</code> is no longer allowed because this would exclude the extensions for this call. The Scout SDK marks such calls with error markers in the Eclipse Problems view. If really required the corresponding intercept-Method can be used. So instead directly calling <code>myField.execChangedValue</code> you may call <code>myField.interceptChangedValue()</code>.
+

Revision as of 11:02, 29 June 2016

The Scout documentation has been moved to https://eclipsescout.github.io/.

Moved to new Scout documentation at http://eclipsescout.github.io/6.0/technical-guide.html#texts

Back to the top