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/Documentation"

m (redirection to new documentation site added)
 
(28 intermediate revisions by 9 users not shown)
Line 1: Line 1:
 +
#REDIRECT [[Scout/Concepts]]
 +
{{ScoutPage|cat=Documentation}}
 +
 
= Overview  =
 
= Overview  =
  
 
= Architecture  =
 
= Architecture  =
  
[[Image:Scout architecture overview.png]]  
+
[[Image:Scout architecture overview.png|thumb|right|200px|Scout Architecture Overview]]  
  
 
== Main Parts of a Scout Project  ==
 
== Main Parts of a Scout Project  ==
Line 11: Line 14:
 
= Client / Server Communication  =
 
= Client / Server Communication  =
  
Client and backend communicate using the HTTP protocol or its extension HTTPS, providing Transport Layer Security (TLS). Client requests are sent as HTTP POST requests, carrying SOAP messages as payload. <br/>
+
Client and backend communicate using the HTTP protocol or its extension HTTPS, providing Transport Layer Security (TLS). Client requests are sent as HTTP POST requests, carrying SOAP messages as payload. <br> Although the interface looks like a standard web service, there is no WSDL interface description for internal Scout services. The interface is extracted directly out of implementing classes using Java serialization. The serialized service invocation and its result are written into the SOAP message’s body. The SOAP body is enriched with additional plain-text elements, since the serialized data looks obscured for external parties being involved in the transportation chain (e.g. content filters). This communication scheme is called mixed-mode.<br> Scout provides support out of the box for switching to an exclusive XML-based communication scheme. However performance will be impacted by verbose messages exchanged between client and backend and the interfaces are still not described by WSDL documents (actually the data is just serialized into XML). Performance will decrease with a factor of about 3 to 5 times.<br> Additionally Scout makes use of HTTP sessions for performance reasons. Using the session notion contradicts common web service principles, demanding for stateless service implementations.
Although the interface looks like a standard web service, there is no WSDL interface description for internal Scout services. The interface is extracted directly out of implementing classes using Java serialization. The serialized service invocation and its result are written into the SOAP message’s body. The SOAP body is enriched with additional plain-text elements, since the serialized data looks obscured for external parties being involved in the transportation chain (e.g. content filters). This communication scheme is called mixed-mode.<br/>
+
 
Scout provides support out of the box for switching to an exclusive XML-based communication scheme. However performance will be impacted by verbose messages exchanged between client and backend and the interfaces are still not described by WSDL documents (actually the data is just serialized into XML). Performance will decrease with a factor of about 3 to 5 times.<br/>
+
Several switches exist to control how a Scout client handles [[Proxy settings]].  
Additionally Scout makes use of HTTP sessions for performance reasons. Using the session notion contradicts common web service principles, demanding for stateless service implementations.
+
  
 
== Service Tunnel  ==
 
== Service Tunnel  ==
  
Proxy services, Service Factories Entry point server (servlet) Outgoing point client  
+
[[Image:ScoutRemoteServices.png|thumb|right|200px|Scout Remote Services]]
 +
 
 +
Scout client - server communication is designed so it can be used completely transparent and easy. Although the communication is based on SOAP it is not necessary to define a WSDL for each service and create their stubs on the client. Instead those remote services are accessible through the OSGi service registry like any other (client) service. The key of that simple but yet powerful concept is called "dynamic proxy".
 +
 
 +
The steps to create such a remote service are as follows:
 +
 
 +
#Create a service interface in the shared plugin
 +
#Implement the service in the server plugin
 +
#Register the implementation as scout server service in the sever plugin
 +
#Register the service interface as scout client service proxy in the client plugin
 +
 
 +
=== Request Response Roundtrip  ===
 +
 
 +
[[Image:ScoutRemoteServiceCall.png|thumb|right|200px|Scout Remote Service Call]]
 +
 
 +
At the core of the scout client proxy service registry a new interface of the desirecd type is created as a reflection java proxy with a handler that delegates all invocations to that interface to the HttpServiceTunnel: ServiceTunnelInvocationHandler.invoke() --&gt; HttpServiceTunnel.tunnelOnline().
 +
 
 +
The HttpServiceTunnel wraps the invocation as HttpServletRequest and sends it via IServiceTunnelContentHandler to the server servlet with path /process. In the server the path /process is mapped in the plugin.xml to the ServiceTunnelServlet.
 +
 
 +
Before the call is received by the servlet it has to pass the active servlet filters, one after another. These filters are also defined in the plugin.xml of the server and typically used for authentication checks. The BasicSecurityFilter for example enforces a valid username/password using HTTP BASIC Auth.
 +
 
 +
Finally the ServiceTunnelServlet is receiving the request. At that moment the code is running inside a JAAS security context due to the SecurityFilter. Before delegating the call to the actual service the ServiceTunnelServlet deserializes the request using the same IServiceTunnelContentHandler as the client, creates and loads the ServerSession and also creates a ServerJob which opens a transaction for the incoming request. That ServerJob delegates the call to the ITransactionDelegate which invokes the actual service.
 +
 
 +
When the processing is finished the transaction will be committed and the response serialized by the IServiceTunnelContentHandler and sent back to the client.
 +
 
 +
=== Input/Output Validation ===
 +
By default there is no input and output validation of incoming and outgoing remote service calls done.
 +
 
 +
In order to activate central input/output validation, create and use a subclass of ServiceTunnelServlet and override runServerJobTransactionWithDelegate().
 +
 
 +
There use a subclass of DefaultTransactionDelegate with implemented methods filterInput() and filterOutput().
 +
The easiest way to implement is by calling new DefaultValidator().validate();
 +
The DefaultValidator validates all arguments using a deep-traversal check. Form datas (subclass of AbstarctFormData) are checked with the delegate DefaultFormDataValidator.
 +
 
 +
This is very useful since the eclipse scout sdk writes all ui model properties relevant for validation automatically to the form data.
 +
 
 +
For example a StringField in a form defining a maxLength of 200 by
 +
 
 +
<source lang="java">
 +
public class LastName extends AbstractStringField {
 +
  ...
 +
  protected int getConfiguredMaxLength(){
 +
    return 200;
 +
  }
 +
}
 +
</source>
 +
 
 +
will result in a form data field
 +
 
 +
<source lang="java">
 +
public class LastName extends AbstractValueFieldData<String> {
 +
  ...
 +
  public static final HashMap<String,Object> validationRules=new HashMap<String,Object>();
 +
  static{
 +
    validationRules.put("maxLength",200);
 +
  }
 +
}
 +
</source>
 +
 
 +
 
 +
 
 +
That way you define your business rules exactly once and they are automatically checked at the central input validation on the server, since the shared form data classes are used on client and server.
 +
 
 +
For more details see ValidationRule, ValidationStrategy, IValidator.
  
 
=== Message Structure  ===
 
=== Message Structure  ===
 +
 
Base64 encoded Serialized objects  
 
Base64 encoded Serialized objects  
  
The request parameters as well as the response data can be transmitted in different customizable formats (contents of the <data>...</data> tags). Scout provides support out of the box for switching to an exclusive XML-based communication scheme.  
+
The request parameters as well as the response data can be transmitted in different customizable formats (contents of the &lt;data&gt;...&lt;/data&gt; tags). Scout provides support out of the box for switching to an exclusive XML-based communication scheme.  
  
 
==== Request Message  ====
 
==== Request Message  ====
Line 32: Line 98:
 
#Service arguments  
 
#Service arguments  
 
#Information like timestamp, TCP/IP origin, varia (xsd:any)
 
#Information like timestamp, TCP/IP origin, varia (xsd:any)
 
+
<pre>Mime-type: application/soap+xml
<pre>
+
&lt;?xml version="1.0" encoding="UTF-16"?&gt;
Mime-type: application/soap+xml
+
&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  
<?xml version="1.0" encoding="UTF-16"?>
+
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  
+
   &lt;SOAP-ENV:Body&gt;
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+
     &lt;request version="3.0.0" format="de_CH" language="de_CH"  
   <SOAP-ENV:Body>
+
       service="com.bsiag.scout.shared.services.common.ping.IPingService" operation="ping"/&gt;
     <request version="3.0.0" format="de_CH" language="de_CH"  
+
     &lt;data&gt;&lt;/data&gt;
       service="com.bsiag.scout.shared.services.common.ping.IPingService" operation="ping"/>
+
     &lt;info ts="20080715114301917" origin="192.168.1.105"&gt;&lt;/info&gt;
     <data></data>
+
   &lt;/SOAP-ENV:Body&gt;
     <info ts="20080715114301917" origin="192.168.1.105"></info>
+
&lt;/SOAP-ENV:Envelope&gt;
   </SOAP-ENV:Body>
+
</pre>  
</SOAP-ENV:Envelope>
+
</pre>
+
 
+
 
==== Response Message  ====
 
==== Response Message  ====
  
Line 56: Line 119:
  
 
Example with Status: „OK“  
 
Example with Status: „OK“  
 
+
<pre>Mime-type: application/soap+xml  
<pre>
+
&lt;?xml version="1.0" encoding="UTF-16"?&gt;
Mime-type: application/soap+xml  
+
&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  
<?xml version="1.0" encoding="UTF-16"?>
+
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  
+
   &lt;SOAP-ENV:Body&gt;
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+
     &lt;response status="OK" type="String"/&gt;
   <SOAP-ENV:Body>
+
     &lt;data&gt;&lt;/data&gt;
     <response status="OK" type="String"/>
+
     &lt;info ts="20080715114301917" origin="192.168.3.2"&gt;&lt;/info&gt;
     <data></data>
+
   &lt;/SOAP-ENV:Body&gt;
     <info ts="20080715114301917" origin="192.168.3.2"></info>
+
&lt;/SOAP-ENV:Envelope&gt;
   </SOAP-ENV:Body>
+
</pre>  
</SOAP-ENV:Envelope>
+
</pre>
+
 
+
 
Example with Status „ERROR“  
 
Example with Status „ERROR“  
 
+
<pre>Mime-type: application/soap+xml  
<pre>
+
&lt;?xml version="1.0" encoding="UTF-16"?&gt;
Mime-type: application/soap+xml  
+
&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  
<?xml version="1.0" encoding="UTF-16"?>
+
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"&gt;
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"  
+
   &lt;SOAP-ENV:Body&gt;
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
+
     &lt;response status="ERROR"&gt;
   <SOAP-ENV:Body>
+
       &lt;exception type="SecurityException"&gt;Access denied&lt;/exception&gt;
     <response status="ERROR">
+
     &lt;/response&gt;
       <exception type="SecurityException">Access denied</exception>
+
     &lt;data&gt;&lt;/data&gt;
     </response>
+
     &lt;info ts="20080715114301917" origin="192.168.3.2"&gt;&lt;/info&gt;
     <data></data>
+
   &lt;/SOAP-ENV:Body&gt;
     <info ts="20080715114301917" origin="192.168.3.2"></info>
+
&lt;/SOAP-ENV:Envelope&gt;
   </SOAP-ENV:Body>
+
</SOAP-ENV:Envelope>
+
 
</pre>
 
</pre>
 
+
 
== Session Handling  ==
 
== Session Handling  ==
  
Line 96: Line 154:
  
 
= Client Concepts  =
 
= Client Concepts  =
<p>The Scout UI architecture provides for orchestration. All entities (Forms, TablePages, ...) contain their component elements as inner classes, therefore entities are kept together as single unit.</p>
+
 
<p>The abstract component model creates a type hierarchy for component elements by subclassing which also supports for automated and central control of recurring tasks as validation, parsing, etc. for each type and in the same time provides for consistent templates.</p>
+
The Scout UI architecture provides for orchestration. All entities (Forms, TablePages, ...) contain their component elements as inner classes, therefore entities are kept together as single unit.
 +
 
 +
The abstract component model creates a type hierarchy for component elements by subclassing which also supports for automated and central control of recurring tasks as validation, parsing, etc. for each type and in the same time provides for consistent templates.
  
 
== Separation of UI and GUI  ==
 
== Separation of UI and GUI  ==
Another reason to create an own component model was to decouple the complex GUI code which needed to be implemented only once, as a result. So what we actually get is a separation of UI and GUI where the business logic for the UI is independent of any specific GUI implementation. This leads to the fact that you can easily switch between Swing, SWT or any other upcoming GUI implementation if you like.
 
  
[[Image:Scout gui representation.png]]  
+
[[Image:Scout gui representation.png|thumb|right|200px|Scout GUI Representation]]  
<p>
+
 
In the picture you can see how the UI and the GUI of scout and your application based on scout work together. On the left there is the plugin org.eclipse.scout.rt.client (UI) and the plugin org.eclipse.scout.rt.ui.swt (GUI). The UI plugin does not have any dependency to the GUI plugin so that it stays independent. On the right side there are the counterparts of the scout plugins which form your applications client. </p>
+
Another reason to create an own component model was to decouple the complex GUI code which needed to be implemented only once, as a result. So what we actually get is a separation of UI and GUI where the business logic for the UI is independent of any specific GUI implementation. This leads to the fact that you can easily switch between Swing, SWT or any other upcoming GUI implementation if you like.
 +
 
 +
In the picture you can see how the UI and the GUI of scout and your application based on scout work together. On the left there is the plugin org.eclipse.scout.rt.client (UI) and the plugin org.eclipse.scout.rt.ui.swt (GUI). The UI plugin does not have any dependency to the GUI plugin so that it stays independent. On the right side there are the counterparts of the scout plugins which form your applications client.  
  
 
== Job Queue  ==
 
== Job Queue  ==
Line 110: Line 171:
 
== Component Model  ==
 
== Component Model  ==
  
[[Image:Client components.png]]  
+
[[Image:Client components.png|thumb|right|200px|Client Components]]  
  
 
=== Client Session  ===
 
=== Client Session  ===
Line 137: Line 198:
 
A form is both a model structure of a ui concept known as dialog or view and also a model of a wizard page. Wizard buttons are added automatically to the main box if missing.  
 
A form is both a model structure of a ui concept known as dialog or view and also a model of a wizard page. Wizard buttons are added automatically to the main box if missing.  
  
==== Form Handler ====
+
==== Form Handler ====
<p>Every form is started by a FormHandler. A form handler is state less, the state is only held on the form itself.  
+
 
Form handlers provide for a controlled form lifecycle. Default operations are:</p>
+
Every form is started by a FormHandler. A form handler is state less, the state is only held on the form itself. Form handlers provide for a controlled form lifecycle. Default operations are:
*execLoad: Before the form is activated, this method loads its data. All field changes done here appear as unchanged in the form.
+
 
*execPostLoad: Called after execLoad and before the form is activated. When field values are changed here, these appear as changed.
+
*execLoad: Before the form is activated, this method loads its data. All field changes done here appear as unchanged in the form.  
*execCheckFields: This method is called in order to check field validity. This method is called just after the IForm.execCheckFields() but before the form is validated and stored. After this method, the form is checking fields itself and displaying a dialog with missing and invalid fields.
+
*execPostLoad: Called after execLoad and before the form is activated. When field values are changed here, these appear as changed.  
*execValidate: This method is called in order to update derived states like button enabling. This method is called after the IForm.execValidate() but before the form is stored.
+
*execCheckFields: This method is called in order to check field validity. This method is called just after the IForm.execCheckFields() but before the form is validated and stored. After this method, the form is checking fields itself and displaying a dialog with missing and invalid fields.  
*execStore: When the form is closed using Ok, Save, Search, Next, etc.. this method is called to apply the changes to the persistency layer
+
*execValidate: This method is called in order to update derived states like button enabling. This method is called after the IForm.execValidate() but before the form is stored.  
*execDiscard: When the form is closed using cancel or close this method is called to manage the case that no changes should be performed (revert case)
+
*execStore: When the form is closed using Ok, Save, Search, Next, etc.. this method is called to apply the changes to the persistency layer  
 +
*execDiscard: When the form is closed using cancel or close this method is called to manage the case that no changes should be performed (revert case)  
 
*execFinally: When the form is closed in any way this method is called to dispose of resources or deallocate services
 
*execFinally: When the form is closed in any way this method is called to dispose of resources or deallocate services
  
Line 193: Line 255:
  
 
== Transaction Handling  ==
 
== Transaction Handling  ==
 +
Basically every request to the server is one transaction. This transaction is created by the servlet which receives the request. If the processing was successful (which means the service did not throw an exception) the transaction will be committed when the response is sent to the client.
 +
 +
The servlet which is responsible for that is called ServiceTunnelServlet and registered at /process. The transaction runs under the user's (JAAS) Subject with a ITransaction in the thread context. ThreadContext.get(ITransaction) delivers the "current" transaction. The wrapper of the transaction is always a scout ServerJob. Every resource taking part in the transaction (similar to xa) can register a ITransactionMember to the transaction. Once the transaction is closed it is either committed (2-phase) or rolled back based on the property ITransaction.getFailures() that must be null for a commit.
  
 
== Configuration  ==
 
== Configuration  ==
Line 199: Line 264:
  
 
=== Statement Builder  ===
 
=== Statement Builder  ===
 +
 +
See {{ScoutLink|Concepts|StatementBuilder|FormDataStatementBuilder}}.
  
 
== config.ini  ==
 
== config.ini  ==
Line 244: Line 311:
 
= Security  =
 
= Security  =
  
== Authentication / Authorization ==
+
In order to understand this chapter it is important to know the difference between the two terms "Authentication" and "Authorization".
  
;Failover Switch
+
;Authentication
:Determines whether the next filter should be used to try to authenticate / authorize the user.<br>The last filter has to set this to <code>false</code>
+
:Authentication means identifing the user trying to access the system.
;Authentication
+
;Authorization  
:Identify the user trying to access the system  
+
:Authorization means determing the users rights and permissions according to his identity.
;Authorization
+
:Determine the users rights and permissions according to his identity
+
  
<br>
+
== Security principles of scout ==
  
=== Security Filters  ===
+
Scout security actually relies on concepts of the java’s standard and pluggable authentication and authorization service (JAAS) by representing the source of a request as a subject with principals associated. In turn, any action is performed on behalf of that subject in a respective doAs-call.
 +
Instead of using the core JAAS classes for authentication (LoginContext, LoginModule, CallbackHandler and Callback), Scout authenticates users directly in so called SecurityFilters. Those filters are chainable, meaning that you might have several filters knowing of how to authenticate user credentials against database (DataSourceSecurityFilter), LDAP directory (LDAPSecurityFilter) or whatever you like. Those filters are registered as an extension to the extension point 'org.eclipse.scout.http.servletfilter.filters'. If a filter can authenticate the user successfully, the request is passed within a doAs call to the endpoint servlet (ServiceTunnelServlet) to dispatch the call to the requested OSGi service.
  
The security filters need to be included as extensions in the server's <code>plugin.xml</code> as extensions. The configuration of these extensions points works as defaults, but is overriden by the [[#Settings_in_config.ini]].  
+
Because Scout does not use the LoginContext for authentication and is not based on the static policy configuration files for principal-based granting (Policy API), permission loading is done by Scout AccessControlService. There are gathered all the permissions that belong to the authenticated subject and are maintained in Scout AccessControlStore. In consequence, authorization cannot be delegated to JAAS AccessController, but is done in a very convenient way by Scout AccessControlService itself.
  
{| border="1" cellpadding="3" cellspacing="0"
+
== Authentication ==
|-
+
Let us have a look on how the system prompts for authentication.
! BasicSecurityFilter
+
First you have to know, that all code on client is run within a doAs call with the user's account name as subject. By default, authentication is only triggered by calling the backend. That means that if you have a standalone client application without a server running, you are never prompted for authentication.
! <code>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter</code>
+
So when doing your first backend call, a connection to the server is established. In the application's life cycle, this typically occurs if initializing the client session which is quite the first thing done. Thereby, the security filter on server side intercepts the call. Because no subject is associated yet, a 401 Unauthorized error code is sent back to the client. On client side, before being propagated back to the caller, this 401 is intercepted by the installed net authenticator. By default, the installed authenticator first looks in the Eclipse secure preference store for credentials. If not available, a login dialog is presented to the user to enter username and password. In turn, those are sent back to the server to continue authentication.
|-
+
Please note, that the authenticator mechanism can be installed in your Swing application by calling NetActivator.install().
| The basic filter with the usernames and passwords directly configured in the configuration file (or the extension point).  
+
 
 +
<source lang="java">
 +
@Override
 +
protected Object startInSubject(IApplicationContext context) throws Exception {
 +
  NetActivator.install();
 +
  return super.startInSubject(context);
 +
}
 +
</source>
 +
 
 +
Thereby, the default Scout authenticator InternalNetAuthenticator is installed. This can be easily overwritten by registering an OSGi service with the name java.net.Authenticator and a ranking higher than -2. Alternatively, you can register an extension to the Eclipse extension point org.eclipse.core.net.authenticator that contains your custom java.net.Authenticator.
 +
 
 +
=== Security Filters  ===
 +
Scout Security Filters are responsible for the authentication of the user. They can simply assigned to on ore more servlets by adding an extension of extension point 'org.eclipse.scout.http.servletfilter.filters'. Since the filters are chainable an order can be set. Additionally it can be defined if the filter should be active or not. This behaviour can easily be overriden by using the config.ini (see [[#Settings_in_config.ini]]).  
 +
 
 +
Following SecurityFilters come with scout and can be found in the package org.eclipse.scout.http.servletfilter.security. If you need a custom behaviour (like using the subject provided by an application server) just create your own filter.
 +
;AnonymousSecurityFilter
 +
A security filter allowing anonymous access to the application
 +
;BasicSecurityFilter
 +
The basic filter with the usernames and passwords directly configured in the configuration file (or the extension point).  
 
E.g.:  
 
E.g.:  
 
<pre>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#users=frank\=test,mark\=test,steve\=test
 
<pre>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#users=frank\=test,mark\=test,steve\=test
 
</pre>  
 
</pre>  
|
+
;DataSourceSecurityFilter  
|-
+
Uses the users and passwords defined in the database.  
! DataSourceSecurityFilter  
+
;LDAPSecurityFilter
! <code>org.eclipse.scout.rt.server.servlet.filter.DataSourceSecurityFilter</code>
+
A security filter to authenticate against a ldap server.
|-
+
| Uses the users and passwords defined in the database.  
+
|
+
|}
+
  
 
==== Settings in config.ini  ====
 
==== Settings in config.ini  ====
  
For every filter you can set the four options (example with the <code>BasicSecurityFilter</code>).  
+
In order to activate or deactivate a certain filter in a specific environment you typically set the property "active" to true or false in the config.ini belonging to the environment. Lets say you would like to use the BasicSecurityFilter in development environment but not in production, instead you would like to use DataSourceSecurityFilter. Good practice is to define DataSourceSecurityFilter as active in your plugin.xml and BasicSecurityFilter as inactive. In the config.ini assigned to the development product you can override this defaults and inactivate DataSourceSecurityFilter and activate BasicSecurityFilter.
<pre>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/process#active=false
+
<pre>
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/updatesite#active=false
+
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/process#active=true
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/test#active=false
+
org.eclipse.scout.rt.server.servlet.filter.DataSourceSecurityFilter/process#active=false
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/services#active=true
+
</pre>
</pre>  
+
The settings determine, whether the security filter is used for a given servlet path. The often used paths are:
+
  
;process
+
Please note: You can activate/deactive the filters for a specific servlet path. If the property for a given servlet is not explicitly set the default of the plugin.xml is used.
:This is where all Scout internals are connect, i.e. the calls from the client to the server.  
+
 
;updatesite
+
/process is typically assigned to the main entry point servlet called ServiceTunnelServlet. This is where all scout internals connect, i.e. the calls from the client to the server.  
:Authentication for users connecting to the updatesite (via browser) to download the client. This also includes the automatic updates of the client.  
+
 
;test
+
Another property of the servlet filter is called "failover".
:Authentication for test servlets
+
This property determines whether the filter should deny access, if not successfully authenticated (<code>false</code>) or if the next filter should try to authenticate (<code>true</code>).  
;services
+
:Authentication for webservices, this will probably always be the <code>BasicSecurityFilter</code> as the other side's implementation has the credentials configured somewhere as well.
+
 
<pre>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#failover=true
 
<pre>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#failover=true
 
</pre>  
 
</pre>  
Determines, whether the filter should deny access, if not successfully authenticated (<code>false</code>) or if the next filter should try to authenticate (<code>true</code>).
 
  
=== Example Setups  ===
+
== Authorization (Granting) ==
  
== Granting ==
+
Granting access to a specific resource is done with permissions. Permissions represent atomic data and operation-level security tokens. They are checked before a privileged operation is performed.
  
Describe the Permisssion Classes, the permission store, ACCESS....  
+
=== Access restriction on CRUD operations ===
 +
Typically there are four different permissions per entity for performing CRUD operations on an object. With this four permissions you are able to restrict the access to that entity for create, read, update and delete operations.
  
= Utilities  =
+
Let us assume you require a permission to allow a user to access companies. Thereto, you create the permission class 'ReadCompanyPermission' in the package x.y.shared.security:
  
== Codetypes  ==
+
<source lang="java">
 +
public class ReadCompanyPermission extends BasicPermission {
 +
 
 +
  public ReadCompanyPermission() {
 +
    super("ReadCompany");
 +
  }
 +
}
 +
</source>
 +
As you can see ReadCompanyPermission extends from BasicPermission which is a class of the package java.security provided by java itself. Every permission in a scout project needs to extend from that class or rather from java.security.Permission directly.
  
== NLS-Support  ==
+
The name you provide in the constructor is the name of the permission. Among other things, it is used to decide whether to grant access to a specific resource. For more detail, please refer to BasicPermission#implies(Permission p) which is evaluated in IAccessControlService#checkPermission(Permission p).
  
to do
+
With the help of that ReadCompanyPermission you are now able to check if the user has the right to read a company entity.
  
== Logging  ==
+
<source lang="java">
 +
if (!ACCESS.check(new ReadCompanyPermission())) {
 +
  throw new VetoException("Authorization failed");
 +
}
 +
else {
 +
  // user is authorized, do some business logic here
 +
}
 +
</source>
  
Scout has moved to [http://www.slf4j.org slf4j]. slf4j is a logging facade, which is implemented by [[#Logger_Implementations|various loggers]].  
+
Please note, that the class ACCESS is simply a delegate to IAccessControlService. The IAccessControlService is responsible for loading and caching the permissions as well as checking them. In order to do that it is necessary to specify how the permissions belonging to the current user can be loaded. This is done by overriding the method execLoadPermissions in your AccessControlService.  
  
slf4j also offers bridges that map calls to the "old" loggers to slf4j.  
+
"Your" AccessControlService means you need to have a custom AccessControlService extending AbstractAccessControlService in your project. This is typically created by Scout SDK itself. Furthermore, this service has to be registered in the plugin.xml of the server project similar to:
  
The development environment contains just Logback, impl.simple, impl.nop and the bridges.  
+
<source lang="xml">
 +
<service class="x.y.server.services.custom.security.AccessControlService" factory="org.eclipse.scout.rt.server.services.ServerServiceFactory" session="x.y.server.ServerSession"/>
 +
</source>
  
=== Logger Usage  ===
+
=== How permissions are discovered ===
  
Scout code should only use the <code>IScoutLogger</code>.  
+
Permission are discovered by IPermissionService. The default implementation looks for permission classes in all bundles installed in the OSGi environemnt. Thereby, the following criteria must be satisfied:
<pre>  private static IScoutLogger logger = ScoutLogManager.getLogger(MyOwnClass.class);
+
#The class must be of the type java.security.Permission
</pre>
+
#The type must be a public concrete class, meaning not an interface nor an abstract class
The <code>IScoutLogger</code>-Interface is implemented by <code>SLF4JDelegateLogger</code>, which is returned by the above call to <code>ScoutLogManager.getLogger(Class)</code>. This wrapps one of the various possible log implementations.  
+
#Class must have the token 'Permission' in its name
 +
#The class must be located in a package with '.security.' in its package path
  
In your <code>config.ini</code> you can set the property <code>org.eclipse.scout.commons.logger.level</code>. If set to another value than -1 it will override all other configurations for logging. You will probably want to not set this property and configure the logging in a <code>logback.xml</code> (see [[#Using_slf4j.2FLogback|Using slf4j/Logback]]).  
+
Please note: This behavior can be overwritten by writing an own implementation for IPermissionService.
  
Example:
+
=== Fine-grained access control ===
<pre>### Logging
+
Further, it is possible to use fine-grained access permissions. This is in contrast to the BasicPermission mentioned above, which simply handles 'go' or 'no-go' situations. That is that if the user has the permission, the access to the resource is granted or rejected otherwise.
# sets the default loglevel, -1=INHERITED (definitions from logback-test.xml),
+
#  1=ERROR, 2=WARN, 3=INFO, 4=DEBUG, 5=TRACE, if set and to another value than -1 this will take precedence to all configurations in logback-test.xml
+
org.eclipse.scout.commons.logger.level=2
+
</pre>
+
=== Logger Implementations  ===
+
  
;Logback
+
However, fine-grained access permissions must be of the type BasicHierarchyPermission. The concept is based on various levels in the range from 0 up to 100. Thereby, 0 means no-access, whereas 100 mean full-access. Basically, if the permission of the user (loaded from database) has a level higher or equals than/to the level requested, access is granted.
:The native implementation and a successor to log4j
+
In BasicHierarchyPermission, the following levels are defined:
;impl.simple
+
:Redirection of slf4j calls to std-out
+
;impl.Log4j
+
:Redirection of slf4j calls to log4j
+
;impl.jakarta.commons.logging
+
:Redirection slf4j calls to jcl
+
;impl.Java.util.logging
+
:Redirection of slf4j calls to jul
+
;impl.nop
+
:Redirection of slf4j calls to "nothing"
+
  
=== Using slf4j/Logback  ===
+
<pre>
 +
LEVEL_NONE = 0
 +
LEVEL_ALL = 100
 +
LEVEL_UNDEFINED = -1
 +
</pre>
  
To use slf4j/Logback the following fragments / plugings are required. Include them as dependencies in your product-file.
 
  
*ch.qos.logback.core
+
Again, let us elaborate a tiny example:
*ch.qos.logback.slf4j
+
The requirement would be that users should only access companies which they really belong to. For that purpose, we introduce a new access level LEVEL_OWN=10.
*org.slf4j.api
+
*org.slf4j.ext
+
*org.slf4j.jcl (optional)
+
*org.slf4j.jul (optional)
+
*org.slf4j.log4j (optional)
+
*a dedicated fragment: <code>[[#fragment.name.org|fragment.name.org]]</code>
+
  
Make sure the following dependencies have NOT been included:  
+
The permission ReadCompanyPermission would be changed as follows:
  
*org.slf4j.impl.nop
+
<source lang="java">
*org.slf4j.impl.siml
+
public class ReadCompanyPermission extends BasicHierarchyPermission {
 +
 
 +
  public static final int LEVEL_OWN = 10;
 +
 
 +
  private Long m_companyId;
 +
 
 +
  public ReadCompanyPermission(Long companyId) {
 +
    super("ReadCompany" + "." + companyId, LEVEL_UNDEFINED);
 +
    m_companyId = companyId;
 +
  }
 +
 
 +
  protected boolean execCheckLevel(int userLevel) throws ProcessingException {
 +
    if (userLevel == LEVEL_OWN) {
 +
      return SERVICES.getService(ICompanyService.class).isOwnCompany(getCompanyId());
 +
    }
 +
    return false;
 +
  }
 +
 
 +
  public Long getCompanyId() {
 +
    return m_companyId;
 +
  }
 +
}
 +
</source>
  
Also do '''not''' include fragments implementing log4j or any other logger (these calls will be intercepted by org.slf4j.log4j and forwarded to slf4j).  
+
In order to enable fine-grained access control so the user can only access his personal company objects, the user's permission level of the permission 'ReadCompanyPermission' must be set to 10 when loading the permissions with execLoadPermissions.
  
==== <code>fragment.name.org</code>  ====
+
The access-check to protect the company resource would be changed as follows:
  
Additionally a dedicated fragment is required (the name is up to you of course). The fragment contains the configuration file for logback and a manifest.  
+
<source lang="java">
 +
if (!ACCESS.check(new ReadCompanyPermission(xy))) {
 +
  throw new VetoException("Authorization failed");
 +
}
 +
else {
 +
  // user is authorized, do some business logic here
 +
}
 +
</source>
  
<code>MANIFEST.MF</code>:
+
As you may have noticed, there is provided an implicit level in the ReadCompanyPermission's constructor when doing the super call. This level stands for the minimal required level the user must have in order to access the resource. If you would put LEVEL_ALL in there, the user would not be allowed to access the company resource anymore as his level is only 10 (LEVEL_OWN) which is lower than 100 (LEVEL_ALL).
<pre>Manifest-Version: 1.0
+
In difference, the level LEVEL_UNDEFINED (-1) does not represent a concrete level, but exclusively stands for fine-grained access control. As a consequence, the access controller does not decide by itself whether to grant access or not. Thereto, it delegates this decision to you by invoking execCheckLevel(int userLevel) on the permission.
Bundle-ManifestVersion: 2
+
 
Bundle-Name: Logback Fragment
+
In this example, the provided userLevel would be 10 (LEVEL_OWN). That indicates your code that the caller is only allowed to see companies which he belongs to.
Bundle-SymbolicName: fragment.name.org
+
In consequence, you have to verify the user's relation to the given company and grant access accordingly.
Bundle-Version: 1.0.0.qualifier
+
 
Bundle-Vendor: ....
+
So feel free to define some other fine-grained access levels, e.g. LEVEL_DEPARTMENT = 20;
Fragment-Host: org.slf4j.api;bundle-version="1.6.0"
+
 
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+
Please note, that by calling ACCESS.getLevel(Permission p) the user specific access level on that permission can be requested.
</pre>
+
 
<code>logback-test.xml</code>:
+
= Utilities  =
<pre>&lt;configuration scan="true"&gt;
+
 
  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
+
== Codetypes  ==
    &lt;!-- encoders are assigned the type
+
 
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
+
== NLS-Support  ==
    &lt;encoder&gt;
+
 
      &lt;pattern&gt;%d{ISO8601}&nbsp;%-5level [%thread]&nbsp;%logger:&nbsp;%msg%n&lt;/pattern&gt;
+
to do
    &lt;/encoder&gt;
+
 
  &lt;/appender&gt;
+
== Logging  ==
 +
 
 +
In Eclipse Scout, there exists a transparent way for doing logging. Eclipse Scout supports JUL (using java.util.logging) and the Eclipse Log Framework (using IStatus). Furthermore, a custom log implementation can be registered to use a different Log Framework, f.e. log4j.  
 +
 
 +
=== Logger Usage  ===
 +
 
 +
Project code may use <code>IScoutLogger</code> to abstract from the runtime implementation. <source lang="java">
 +
private static IScoutLogger logger = ScoutLogManager.getLogger(MyOwnClass.class);
 +
</source>
 +
 
 +
It is possible to add a code template to quickly add this line in you class. Here is the pattern:
 +
 
 +
${:import(org.eclipse.scout.commons.logger.IScoutLogger,org.eclipse.scout.commons.logger.ScoutLogManager)}
 +
private static final IScoutLogger logger = ScoutLogManager.getLogger(${enclosing_type}.class);
 +
 
 +
It should appear in the template proposals list (CTRL+Space).
 +
 
 +
<br> The <code>IScoutLogger</code>-Interface is implemented by <code>EclipseLogWrapper</code>, <code>JavaLogWrapper</code> or <code>CustomLogWrapper</code> which is returned by the above call to <code>ScoutLogManager.getLogger(Class)</code>.
 +
 
 +
=== Logger Setup  ===
 +
 
 +
By default, JUL is used. But it is possible to change that behaviour by setting the <code>config.ini</code> property <code>org.eclipse.scout.log</code> or creating a fragment to the host-Plug-In <code>org.eclipse.scout.commons</code>.
 +
 
 +
==== JUL  ====
  
  &lt;logger name="org.eclipse.scout.commons.ConfigIniUtility"&gt;
+
Insert these lines in the <code>config.ini</code> to activate standard java log.
    &lt;level value="WARN" /&gt;
+
<pre>eclipse.consoleLog=false
  &lt;/logger&gt;
+
org.eclipse.scout.log=java
  &lt;root&gt;
+
    &lt;level value="WARN" /&gt;
+
    &lt;appender-ref ref="CONSOLE" /&gt;
+
  &lt;/root&gt;
+
&lt;/configuration&gt;
+
 
</pre>  
 
</pre>  
<code>logback.xml</code>:
 
<pre>&lt;configuration scan="true"&gt;
 
&lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;
 
&lt;file&gt;c:/temp/project.log&lt;/file&gt;
 
  
&lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;
+
===== Using different Log Configurations for Development and Productive Environments in JUL =====
&lt;!-- rollover daily --&gt;
+
&lt;fileNamePattern&gt;c:/temp/project-%d{yyyy-MM-dd}.%i.log&lt;/fileNamePattern&gt;
+
&lt;timeBasedFileNamingAndTriggeringPolicy
+
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"&gt;
+
&lt;!-- or whenever the file size reaches 100MB --&gt;
+
&lt;maxFileSize&gt;100MB&lt;/maxFileSize&gt;
+
&lt;/timeBasedFileNamingAndTriggeringPolicy&gt;
+
  
&lt;!-- keep 30 days' worth of history --&gt;
+
Logging requirements typically differ depending on the target environment.
&lt;maxHistory&gt;30&lt;/maxHistory&gt;
+
&lt;/rollingPolicy&gt;
+
  
&lt;!-- encoders are assigned the type
+
In development a more detailed logging is desirable and logging to the console is just fine, where for productive use, logging should be directed to a file and should concentrate on errors and warnings.  
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
+
&lt;encoder&gt;
+
&lt;pattern&gt;%d{ISO8601}&nbsp;%-5level [%thread]&nbsp;%logger:&nbsp;%msg%n&lt;/pattern&gt;
+
&lt;/encoder&gt;
+
&lt;/appender&gt;
+
  
&lt;logger name="org.eclipse.scout.commons.ConfigIniUtility"&gt;
+
See this {{ScoutLink|HowTo|Setup different Log-Configuration for Production and Development|name=How To}} for implementation details of this scenario.  
&lt;level value="WARN" /&gt;
+
&lt;/logger&gt;
+
&lt;logger name="org.eclipse.scout.rt.server.services.common.sql"&gt;
+
&lt;level value="INFO" /&gt;
+
&lt;/logger&gt;
+
&lt;logger name="org.eclipse.scout.rt.ui.swing.SwingIconLocator"&gt;
+
&lt;level value="WARN" /&gt;
+
&lt;/logger&gt;
+
  
&lt;root&gt;
+
==== Eclipse Log Framework  ====
&lt;level value="INFO" /&gt;
+
 
&lt;appender-ref ref="FILE" /&gt;
+
This is normally used in workbench ui applications with swt.
&lt;/root&gt;
+
<pre>eclipse.consoleLog=true
&lt;/configuration&gt;
+
org.eclipse.scout.log=eclipse
 +
org.eclipse.scout.log.level=WARNING
 
</pre>  
 
</pre>  
<code>build.properties</code>
+
==== Custom Logger (e.g. Log4J) ====
<pre>bin.includes = META-INF/,\
+
              licence.html,\
+
              logback.xml
+
</pre>
+
=== Logback Configuration ===
+
  
==== Different Configuration for Production and Development  ====
+
A custom log implementation can be registered by creating a fragment to the host-Plug-In <code>org.eclipse.scout.commons</code>. In turn, if the config.ini property <code>org.eclipse.scout.log</code> is not set, the ScoutLogManager looks for a class named <code>org.eclipse.scout.commons.logger.CustomLogManager</code> within its classpath. If found, this class is instantiated and used as logging strategy.
  
You probably do not want the same configuration for production / deployment and development. An easy way to solve this is to have two configuration files. A <code>logback.xml</code> for production / deployment and a <code>logback-test.xml</code> for the development (both in the fragment <code>[[#fragment.name.org|fragment.name.org]]</code>). Then you edit the <code>build.properties</code> so the <code>logback-test.xml</code> is not included in the build.
+
As an example the use of log4j is explained in this {{ScoutLink|HowTo|Create_a_CustomLogManager_for_log4j|name=HowTo}}
  
If you start the Scout application from Eclipse, the <code>logback-test.xml</code> takes precedence.
+
=== Additional Logging Features  ===
  
And for the deployment there is only the <code>logback.xml</code>, so nothing to worry about either.  
+
It is possible to set dynamically a global log level to all loggers no matter of which log level they currently are logging. Of course, this can be made undone and all loggers have their origin log level again.  
  
Limitations: This does only allow for two configurations and only one that is deployed. If you have several systems with different configurations you need another solution. You will probably want to have your xml file somewhere else and not included in the application.  
+
Furthermore, it is possible to record log messages for a certain amount of time. When recording is stopped, a file is created containing all log messages since recording was started. The combination of the recording functionality together with the change of the log level at runtime facilitates debugging. This is especially true, if systems are to be maintained to easily get some valuable log output.  
  
==== Automatically Rescanning the Configuration  ====
+
See this {{ScoutLink|HowTo|Make use of the additional Scout logging features|name=How To}} for a detailed example of the additional logging features.
  
The automatic scanning for configuration changes is enabled by the attribute <code>scan="true"</code> in the configuration xml.
+
=== Known issues ===
<pre> &lt;configuration scan="true"&gt;
+
</pre>
+
<br>
+
  
*TODO: Where does it look for the configuration?
+
When using a profiler such as jvisualvm or jconsole with the system property <code>-Dcom.sun.management.jmxremote</code> then the first class loader to load LogManager cannot load the class contained in the logger fragment (even though it is a fragment to the system bundle). This is because the osgi has not even startet yet.
  
 
== Scout Services  ==
 
== Scout Services  ==
  
[[Image:Scout services.png]]
+
[[Image:Scout services.png|thumb|right|200px|Scout Services]]  
  
=== Service Types ===
+
=== Service Types ===
Services are managed by the service registry. There is a variety of service types:
+
 
*Data Services
+
Services are managed by the service registry. There is a variety of service types:  
*Lookup Services
+
 
*Enumeration/Code Services
+
*Data Services  
*Processing Services
+
*Lookup Services  
 +
*Enumeration/Code Services  
 +
*Processing Services  
 
*Workflow services
 
*Workflow services
  
A data service is normally a server service providing read-only data with aggregation and composition logic. Data services offer search and filter capabilities. From the client (frontend) such services are called using service remoting over HTTP(S).
+
A data service is normally a server service providing read-only data with aggregation and composition logic. Data services offer search and filter capabilities. From the client (frontend) such services are called using service remoting over HTTP(S).  
  
A lookup service is normally a server service providing read-only lookup data for dynamic list-of-values such as “Companies”, “Persons”, etc. Lookup Services offer filter and search capabilities and specific data acces “by key”, “by display text”, and “by parent key” (for hierarchical lookup data). From the client these services are called using a lookup service call representing the call data.
+
A lookup service is normally a server service providing read-only lookup data for dynamic list-of-values such as “Companies”, “Persons”, etc. Lookup Services offer filter and search capabilities and specific data acces “by key”, “by display text”, and “by parent key” (for hierarchical lookup data). From the client these services are called using a lookup service call representing the call data.  
  
An enumeration service is normally a server service providing read-only enumeration data for static list-of-values such as “Project State”, “Address Type”, etc. Note that the word “static” does not mean that the data is fixed and constant, but that the character of the data is rather static. Like Lookup services also code services offer filter and search capabilities. From the client these services are called using a code service call representing the call data.
+
An enumeration service is normally a server service providing read-only enumeration data for static list-of-values such as “Project State”, “Address Type”, etc. Note that the word “static” does not mean that the data is fixed and constant, but that the character of the data is rather static. Like Lookup services also code services offer filter and search capabilities. From the client these services are called using a code service call representing the call data.  
  
A processing service is normally a server service providing data manipulation or control operations such as “Company.create”, “Company.modify” etc. From the client these services are called using service remoting over HTTP(S).
+
A processing service is normally a server service providing data manipulation or control operations such as “Company.create”, “Company.modify” etc. From the client these services are called using service remoting over HTTP(S). Most processing services managed by Scout SDK are the backend of UI form models. In order to maximally assist developers, Scout SDK can automatically create a value structure for every UI form that is created and also generate a load/store/create processing service for it.  
Most processing services managed by Scout SDK are the backend of UI form models. In order to maximally assist developers, Scout SDK can automatically create a value structure for every UI form that is created and also generate a load/store/create processing service for it.
+
  
A workflow service is normally a server service providing state machine and workflow control operations such as “AddressChange.start”, “AddressChange.nextStep” etc. From the client these services are called using service remoting over HTTP(S).
+
A workflow service is normally a server service providing state machine and workflow control operations such as “AddressChange.start”, “AddressChange.nextStep” etc. From the client these services are called using service remoting over HTTP(S). Most workflow services managed by Scout SDK are the backend of UI wizard models. In order to maximally assists developers, Scout SDK automatically creates a value structure for every UI wizard that is created and also generates a workflow service for it.  
Most workflow services managed by Scout SDK are the backend of UI wizard models. In order to maximally assists developers, Scout SDK automatically creates a value structure for every UI wizard that is created and also generates a workflow service for it.
+
  
 
== scout.commons  ==
 
== scout.commons  ==
Line 570: Line 660:
 
</pre>  
 
</pre>  
 
== Client Notification  ==
 
== Client Notification  ==
In some circumstances it is necessary to inform the clients about something important. An example could be an incoming call for a specific client or just a request to reload the code type cache. Such requests from server to client are called client notification.
 
  
Because Scout does its client-server communication over https it is limited to a one-way connection. That means, the server can’t directly call the client, it can only answer him. Therefore the client asks the server about new information after a certain time period. The server delivers all interesting notifications for this client. The ClientNotificationFilter defines for which client a notification is interesting. So we basically have a queue with pairs of ClientNotifications and ClientNotificationFilters on the server-side and each time a client calls for information, the server iterates over this queue. Of course these pairs only have a limited time to live, which is also decided by the implementation of the ClientNotificationFilter.
+
[[Image:ClientNotificationOverview.png|thumb|right|200px|Client Notification Overview]]
 +
 
 +
In some circumstances it is necessary to inform the clients about something important. An example could be an incoming call for a specific client or just a request to reload the code type cache. Such requests from server to client are called client notification.
 +
 
 +
Because Scout does its client-server communication over https it is limited to a one-way connection. That means, the server can’t directly call the client, it can only answer him. Therefore the client asks the server about new information after a certain time period. The server delivers all interesting notifications for this client. The ClientNotificationFilter defines for which client a notification is interesting. So we basically have a queue with pairs of ClientNotifications and ClientNotificationFilters on the server-side and each time a client calls for information, the server iterates over this queue. Of course these pairs only have a limited time to live, which is also decided by the implementation of the ClientNotificationFilter.  
  
The ClientNotificationConsumer will periodically ask the server for new ClientNotifications. There is the possibility to tune the polling interval or the blocking timeout. The polling interval describes after how many milliseconds the ClientNotificationConsumer will do his next polling and the blocking timeout defines how long the call of the ClientNotificationConsumer will wait on the backend and look for new ClientNotifications.
+
The ClientNotificationConsumer will periodically ask the server for new ClientNotifications. There is the possibility to tune the polling interval or the blocking timeout. The polling interval describes after how many milliseconds the ClientNotificationConsumer will do his next polling and the blocking timeout defines how long the call of the ClientNotificationConsumer will wait on the backend and look for new ClientNotifications.  
  
Because you don’t want to get all available ClientNotifications from the server, a ClientNotification can only be provided together with a ClientNotificationFilter. This filter defines how long a ClientNotification is active, if it’s multicast or not and who is interested in this ClientNotification.
+
For better performance and lesser HTTP calls, all client notifications that accumulated during a remote service call on the server are collected and attached to the response of the remote service call as additional payload. That means if the client does frequent server calls the client notification poll interval can be decreased.
It is recommended to define your ClientNotificationFilter on the server side, because then you have access to session relevant information like user number or session id. For trivial cases like the comparison of the user number there exists a bunch of default ClientNotificationFilters.
+
  
[[Image:ClientNotificationOverview.png]]
+
Because you don’t want to get all available ClientNotifications from the server, a ClientNotification can only be provided together with a ClientNotificationFilter. This filter defines how long a ClientNotification is active, if it’s multicast or not and who is interested in this ClientNotification. It is recommended to define your ClientNotificationFilter on the server side, because then you have access to session relevant information like user number or session id. For trivial cases like the comparison of the user number there exists a bunch of default ClientNotificationFilters.
  
 
= Scout SDK  =
 
= Scout SDK  =

Latest revision as of 15:37, 1 November 2011

Redirect to:

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

Contents

Overview

Architecture

Scout Architecture Overview

Main Parts of a Scout Project

Client,shared,swt,server Equinox client server

Client / Server Communication

Client and backend communicate using the HTTP protocol or its extension HTTPS, providing Transport Layer Security (TLS). Client requests are sent as HTTP POST requests, carrying SOAP messages as payload.
Although the interface looks like a standard web service, there is no WSDL interface description for internal Scout services. The interface is extracted directly out of implementing classes using Java serialization. The serialized service invocation and its result are written into the SOAP message’s body. The SOAP body is enriched with additional plain-text elements, since the serialized data looks obscured for external parties being involved in the transportation chain (e.g. content filters). This communication scheme is called mixed-mode.
Scout provides support out of the box for switching to an exclusive XML-based communication scheme. However performance will be impacted by verbose messages exchanged between client and backend and the interfaces are still not described by WSDL documents (actually the data is just serialized into XML). Performance will decrease with a factor of about 3 to 5 times.
Additionally Scout makes use of HTTP sessions for performance reasons. Using the session notion contradicts common web service principles, demanding for stateless service implementations.

Several switches exist to control how a Scout client handles Proxy settings.

Service Tunnel

Scout Remote Services

Scout client - server communication is designed so it can be used completely transparent and easy. Although the communication is based on SOAP it is not necessary to define a WSDL for each service and create their stubs on the client. Instead those remote services are accessible through the OSGi service registry like any other (client) service. The key of that simple but yet powerful concept is called "dynamic proxy".

The steps to create such a remote service are as follows:

  1. Create a service interface in the shared plugin
  2. Implement the service in the server plugin
  3. Register the implementation as scout server service in the sever plugin
  4. Register the service interface as scout client service proxy in the client plugin

Request Response Roundtrip

Scout Remote Service Call

At the core of the scout client proxy service registry a new interface of the desirecd type is created as a reflection java proxy with a handler that delegates all invocations to that interface to the HttpServiceTunnel: ServiceTunnelInvocationHandler.invoke() --> HttpServiceTunnel.tunnelOnline().

The HttpServiceTunnel wraps the invocation as HttpServletRequest and sends it via IServiceTunnelContentHandler to the server servlet with path /process. In the server the path /process is mapped in the plugin.xml to the ServiceTunnelServlet.

Before the call is received by the servlet it has to pass the active servlet filters, one after another. These filters are also defined in the plugin.xml of the server and typically used for authentication checks. The BasicSecurityFilter for example enforces a valid username/password using HTTP BASIC Auth.

Finally the ServiceTunnelServlet is receiving the request. At that moment the code is running inside a JAAS security context due to the SecurityFilter. Before delegating the call to the actual service the ServiceTunnelServlet deserializes the request using the same IServiceTunnelContentHandler as the client, creates and loads the ServerSession and also creates a ServerJob which opens a transaction for the incoming request. That ServerJob delegates the call to the ITransactionDelegate which invokes the actual service.

When the processing is finished the transaction will be committed and the response serialized by the IServiceTunnelContentHandler and sent back to the client.

Input/Output Validation

By default there is no input and output validation of incoming and outgoing remote service calls done.

In order to activate central input/output validation, create and use a subclass of ServiceTunnelServlet and override runServerJobTransactionWithDelegate().

There use a subclass of DefaultTransactionDelegate with implemented methods filterInput() and filterOutput(). The easiest way to implement is by calling new DefaultValidator().validate(); The DefaultValidator validates all arguments using a deep-traversal check. Form datas (subclass of AbstarctFormData) are checked with the delegate DefaultFormDataValidator.

This is very useful since the eclipse scout sdk writes all ui model properties relevant for validation automatically to the form data.

For example a StringField in a form defining a maxLength of 200 by

public class LastName extends AbstractStringField {
  ...
  protected int getConfiguredMaxLength(){
    return 200;
  }
}

will result in a form data field

public class LastName extends AbstractValueFieldData<String> {
  ...
  public static final HashMap<String,Object> validationRules=new HashMap<String,Object>();
  static{
    validationRules.put("maxLength",200);
  }
}


That way you define your business rules exactly once and they are automatically checked at the central input validation on the server, since the shared form data classes are used on client and server.

For more details see ValidationRule, ValidationStrategy, IValidator.

Message Structure

Base64 encoded Serialized objects

The request parameters as well as the response data can be transmitted in different customizable formats (contents of the <data>...</data> tags). Scout provides support out of the box for switching to an exclusive XML-based communication scheme.

Request Message

The request SOAP message consists of:

  1. Service reference, operation, version, formatting, language
  2. Service arguments
  3. Information like timestamp, TCP/IP origin, varia (xsd:any)
Mime-type: application/soap+xml
<?xml version="1.0" encoding="UTF-16"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <request version="3.0.0" format="de_CH" language="de_CH" 
      service="com.bsiag.scout.shared.services.common.ping.IPingService" operation="ping"/>
    <data>…</data>
    <info ts="20080715114301917" origin="192.168.1.105">…</info>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Response Message

The response SOAP message consists of:

  1. Service invocation status, maybe exception type
  2. Service response data
  3. Information like timestamp, TCP/IP origin, varia (xsd:any)

Example with Status: „OK“

Mime-type: application/soap+xml 
<?xml version="1.0" encoding="UTF-16"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <response status="OK" type="String"/>
    <data>…</data>
    <info ts="20080715114301917" origin="192.168.3.2">…</info>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Example with Status „ERROR“

Mime-type: application/soap+xml 
<?xml version="1.0" encoding="UTF-16"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <response status="ERROR">
      <exception type="SecurityException">Access denied</exception>
    </response>
    <data>…</data>
    <info ts="20080715114301917" origin="192.168.3.2">…</info>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Session Handling

ServerSession

SharedContext

ClientSession

Client Concepts

The Scout UI architecture provides for orchestration. All entities (Forms, TablePages, ...) contain their component elements as inner classes, therefore entities are kept together as single unit.

The abstract component model creates a type hierarchy for component elements by subclassing which also supports for automated and central control of recurring tasks as validation, parsing, etc. for each type and in the same time provides for consistent templates.

Separation of UI and GUI

Scout GUI Representation

Another reason to create an own component model was to decouple the complex GUI code which needed to be implemented only once, as a result. So what we actually get is a separation of UI and GUI where the business logic for the UI is independent of any specific GUI implementation. This leads to the fact that you can easily switch between Swing, SWT or any other upcoming GUI implementation if you like.

In the picture you can see how the UI and the GUI of scout and your application based on scout work together. On the left there is the plugin org.eclipse.scout.rt.client (UI) and the plugin org.eclipse.scout.rt.ui.swt (GUI). The UI plugin does not have any dependency to the GUI plugin so that it stays independent. On the right side there are the counterparts of the scout plugins which form your applications client.

Job Queue

Component Model

Client Components

Client Session

The client session is the main entry point for client-server communication.

Desktop

The desktop is the entry point of every Scout client application. It can (may) consist of top-level menus, active message box stack, set of available outline, active outline, active tableview, active detail form, active search form, form stack (swing: dialogs on desktop as JInternalFrames; eclipse: editors or views), dialog stack of modal and non-modal dialogs (swing: dialogs as JDialog, JFrame; eclipse: dialogs in a new Shell).

Outline

Typically a Desktop holds multiple outlines. They represent different entry points for the navigation within the application. For every outline a tree is available which allows navigating within the application.

Sorting of Columns

  1. Oracle (or whatever database you prefer) sorts the columns, if an order by clause is specified. Oracle sorts according to the NLS_SORT parameter, which is set per database and can be overwritten in the session.
    select * from V$NLS_PARAMETERS WHERE PARAMETER = 'NLS_SORT'; tells you the current setting.
  2. If you click on a column (client), Scout does the sort.
    • StringColumn: Scout orders the entries according the NlsLocale (see java.text.Collator)
    • SmartColumn:
      • If isSortCodesByDisplayText() is set, the sort is done by Java according the NlsLocale.
      • otherwise the Sortcode is used.

Form

A form is both a model structure of a ui concept known as dialog or view and also a model of a wizard page. Wizard buttons are added automatically to the main box if missing.

Form Handler

Every form is started by a FormHandler. A form handler is state less, the state is only held on the form itself. Form handlers provide for a controlled form lifecycle. Default operations are:

  • execLoad: Before the form is activated, this method loads its data. All field changes done here appear as unchanged in the form.
  • execPostLoad: Called after execLoad and before the form is activated. When field values are changed here, these appear as changed.
  • execCheckFields: This method is called in order to check field validity. This method is called just after the IForm.execCheckFields() but before the form is validated and stored. After this method, the form is checking fields itself and displaying a dialog with missing and invalid fields.
  • execValidate: This method is called in order to update derived states like button enabling. This method is called after the IForm.execValidate() but before the form is stored.
  • execStore: When the form is closed using Ok, Save, Search, Next, etc.. this method is called to apply the changes to the persistency layer
  • execDiscard: When the form is closed using cancel or close this method is called to manage the case that no changes should be performed (revert case)
  • execFinally: When the form is closed in any way this method is called to dispose of resources or deallocate services

Form fields

Form fields are the basic elements for user inputs fiels within a form. Examples are:

  • TextField
  • SmartField
  • NumberField
  • DateField
  • FileChooser
  • ListBox
  • TreeBox
  • CheckBox
  • RadioButton
  • ToogleButton

Futhermore there exists composites fields like:

  • GroupBox
  • TabBox
  • SequenceBox
  • SnapBox
  • RangeBox
  • RadioButtonGroupBox

Menu

The menu component include all links, functionalities, etc... available within the application.

Tool

Tool component is used for grouping or dividing different views. This can be used for building business views on datas or just structuring your own application.

Wizard

Wizards support a user to work in a process driven approach on a task.


Server Concepts

Server Side Equinox

Jetty, ServerApplication as Startup Point

Transaction Handling

Basically every request to the server is one transaction. This transaction is created by the servlet which receives the request. If the processing was successful (which means the service did not throw an exception) the transaction will be committed when the response is sent to the client.

The servlet which is responsible for that is called ServiceTunnelServlet and registered at /process. The transaction runs under the user's (JAAS) Subject with a ITransaction in the thread context. ThreadContext.get(ITransaction) delivers the "current" transaction. The wrapper of the transaction is always a scout ServerJob. Every resource taking part in the transaction (similar to xa) can register a ITransactionMember to the transaction. Once the transaction is closed it is either committed (2-phase) or rolled back based on the property ITransaction.getFailures() that must be null for a commit.

Configuration

SQL Support

Statement Builder

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

config.ini

Inside of the config.ini in the server it is possible to override the member variables of services.

For example:

com.bsiag.mnet.server.services.common.sql.SqlService#directJ dbcConnection=true


If the service SqlService has a setter method for the member directJdbcConnection then the member has at runtime the value true.

With Scout Eclipse this works for all classes which extends AbstractService

For other classes it must be done by yourself for example with the class FilterConfigInjection at startup.

Server Side Proxy

If the server application needs to access a server in the web and in between your application server and the server in the web is a proxy that needs authentication, you need to set the proxy parameters (like username or password) somewhere.
In the web you find several sites that tell you to start Java with the following options:

-Dhttp.proxyHost=proxyHost
-Dhttp.proxyPort=proxyPort
-Dhttp.proxyUser=proxyUser
-Dhttp.proxyPassword=proxyPassword

(You can set these options in the Tomcat by right-clicking on the Tomcat tray icon, then click on 'Configure...', go to the Java tab & add the four lines to the Java options)
When the request is sent, the proxy host and the proxy port are known & the request is sent over the proxy. However the authentication does not work. Even though these options are loaded when Java / the Tomcat is started.

Either Java does not care about the options for the username and the password or the proxy we use does the authentication not as expected / usual.

If you have problems with the upper solution, you can solve the problem by setting the proxy informations in Java before you send the request (read the proxy informations from the config.ini-file).
Your code could look similar to the following code snippet:

URL url = new URL(myUrl);
URLConnection conn;
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(myProxyHost, myProxyPort));
conn = url.openConnection(proxy);
String encoded = Base64Utility.encode((myProxyUsername + ":" + myProxyPassword).getBytes());
conn.setRequestProperty("Proxy-Authorization", "Basic " + encoded);

However, with this solution you need to set the proxy parameters for each request anew.

Security

In order to understand this chapter it is important to know the difference between the two terms "Authentication" and "Authorization".

Authentication
Authentication means identifing the user trying to access the system.
Authorization
Authorization means determing the users rights and permissions according to his identity.

Security principles of scout

Scout security actually relies on concepts of the java’s standard and pluggable authentication and authorization service (JAAS) by representing the source of a request as a subject with principals associated. In turn, any action is performed on behalf of that subject in a respective doAs-call. Instead of using the core JAAS classes for authentication (LoginContext, LoginModule, CallbackHandler and Callback), Scout authenticates users directly in so called SecurityFilters. Those filters are chainable, meaning that you might have several filters knowing of how to authenticate user credentials against database (DataSourceSecurityFilter), LDAP directory (LDAPSecurityFilter) or whatever you like. Those filters are registered as an extension to the extension point 'org.eclipse.scout.http.servletfilter.filters'. If a filter can authenticate the user successfully, the request is passed within a doAs call to the endpoint servlet (ServiceTunnelServlet) to dispatch the call to the requested OSGi service.

Because Scout does not use the LoginContext for authentication and is not based on the static policy configuration files for principal-based granting (Policy API), permission loading is done by Scout AccessControlService. There are gathered all the permissions that belong to the authenticated subject and are maintained in Scout AccessControlStore. In consequence, authorization cannot be delegated to JAAS AccessController, but is done in a very convenient way by Scout AccessControlService itself.

Authentication

Let us have a look on how the system prompts for authentication. First you have to know, that all code on client is run within a doAs call with the user's account name as subject. By default, authentication is only triggered by calling the backend. That means that if you have a standalone client application without a server running, you are never prompted for authentication. So when doing your first backend call, a connection to the server is established. In the application's life cycle, this typically occurs if initializing the client session which is quite the first thing done. Thereby, the security filter on server side intercepts the call. Because no subject is associated yet, a 401 Unauthorized error code is sent back to the client. On client side, before being propagated back to the caller, this 401 is intercepted by the installed net authenticator. By default, the installed authenticator first looks in the Eclipse secure preference store for credentials. If not available, a login dialog is presented to the user to enter username and password. In turn, those are sent back to the server to continue authentication. Please note, that the authenticator mechanism can be installed in your Swing application by calling NetActivator.install().

@Override
protected Object startInSubject(IApplicationContext context) throws Exception {
   NetActivator.install();
   return super.startInSubject(context);
}

Thereby, the default Scout authenticator InternalNetAuthenticator is installed. This can be easily overwritten by registering an OSGi service with the name java.net.Authenticator and a ranking higher than -2. Alternatively, you can register an extension to the Eclipse extension point org.eclipse.core.net.authenticator that contains your custom java.net.Authenticator.

Security Filters

Scout Security Filters are responsible for the authentication of the user. They can simply assigned to on ore more servlets by adding an extension of extension point 'org.eclipse.scout.http.servletfilter.filters'. Since the filters are chainable an order can be set. Additionally it can be defined if the filter should be active or not. This behaviour can easily be overriden by using the config.ini (see #Settings_in_config.ini).

Following SecurityFilters come with scout and can be found in the package org.eclipse.scout.http.servletfilter.security. If you need a custom behaviour (like using the subject provided by an application server) just create your own filter.

AnonymousSecurityFilter

A security filter allowing anonymous access to the application

BasicSecurityFilter

The basic filter with the usernames and passwords directly configured in the configuration file (or the extension point). E.g.:

org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#users=frank\=test,mark\=test,steve\=test
DataSourceSecurityFilter

Uses the users and passwords defined in the database.

LDAPSecurityFilter

A security filter to authenticate against a ldap server.

Settings in config.ini

In order to activate or deactivate a certain filter in a specific environment you typically set the property "active" to true or false in the config.ini belonging to the environment. Lets say you would like to use the BasicSecurityFilter in development environment but not in production, instead you would like to use DataSourceSecurityFilter. Good practice is to define DataSourceSecurityFilter as active in your plugin.xml and BasicSecurityFilter as inactive. In the config.ini assigned to the development product you can override this defaults and inactivate DataSourceSecurityFilter and activate BasicSecurityFilter.

org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/process#active=true
org.eclipse.scout.rt.server.servlet.filter.DataSourceSecurityFilter/process#active=false

Please note: You can activate/deactive the filters for a specific servlet path. If the property for a given servlet is not explicitly set the default of the plugin.xml is used.

/process is typically assigned to the main entry point servlet called ServiceTunnelServlet. This is where all scout internals connect, i.e. the calls from the client to the server.

Another property of the servlet filter is called "failover". This property determines whether the filter should deny access, if not successfully authenticated (false) or if the next filter should try to authenticate (true).

org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#failover=true

Authorization (Granting)

Granting access to a specific resource is done with permissions. Permissions represent atomic data and operation-level security tokens. They are checked before a privileged operation is performed.

Access restriction on CRUD operations

Typically there are four different permissions per entity for performing CRUD operations on an object. With this four permissions you are able to restrict the access to that entity for create, read, update and delete operations.

Let us assume you require a permission to allow a user to access companies. Thereto, you create the permission class 'ReadCompanyPermission' in the package x.y.shared.security:

public class ReadCompanyPermission extends BasicPermission {
 
  public ReadCompanyPermission() {
    super("ReadCompany");
  }
}

As you can see ReadCompanyPermission extends from BasicPermission which is a class of the package java.security provided by java itself. Every permission in a scout project needs to extend from that class or rather from java.security.Permission directly.

The name you provide in the constructor is the name of the permission. Among other things, it is used to decide whether to grant access to a specific resource. For more detail, please refer to BasicPermission#implies(Permission p) which is evaluated in IAccessControlService#checkPermission(Permission p).

With the help of that ReadCompanyPermission you are now able to check if the user has the right to read a company entity.

if (!ACCESS.check(new ReadCompanyPermission())) {
   throw new VetoException("Authorization failed");
} 
else {
   // user is authorized, do some business logic here
}

Please note, that the class ACCESS is simply a delegate to IAccessControlService. The IAccessControlService is responsible for loading and caching the permissions as well as checking them. In order to do that it is necessary to specify how the permissions belonging to the current user can be loaded. This is done by overriding the method execLoadPermissions in your AccessControlService.

"Your" AccessControlService means you need to have a custom AccessControlService extending AbstractAccessControlService in your project. This is typically created by Scout SDK itself. Furthermore, this service has to be registered in the plugin.xml of the server project similar to:

<service class="x.y.server.services.custom.security.AccessControlService" factory="org.eclipse.scout.rt.server.services.ServerServiceFactory" session="x.y.server.ServerSession"/>

How permissions are discovered

Permission are discovered by IPermissionService. The default implementation looks for permission classes in all bundles installed in the OSGi environemnt. Thereby, the following criteria must be satisfied:

  1. The class must be of the type java.security.Permission
  2. The type must be a public concrete class, meaning not an interface nor an abstract class
  3. Class must have the token 'Permission' in its name
  4. The class must be located in a package with '.security.' in its package path

Please note: This behavior can be overwritten by writing an own implementation for IPermissionService.

Fine-grained access control

Further, it is possible to use fine-grained access permissions. This is in contrast to the BasicPermission mentioned above, which simply handles 'go' or 'no-go' situations. That is that if the user has the permission, the access to the resource is granted or rejected otherwise.

However, fine-grained access permissions must be of the type BasicHierarchyPermission. The concept is based on various levels in the range from 0 up to 100. Thereby, 0 means no-access, whereas 100 mean full-access. Basically, if the permission of the user (loaded from database) has a level higher or equals than/to the level requested, access is granted. In BasicHierarchyPermission, the following levels are defined:

LEVEL_NONE = 0
LEVEL_ALL = 100
LEVEL_UNDEFINED = -1


Again, let us elaborate a tiny example: The requirement would be that users should only access companies which they really belong to. For that purpose, we introduce a new access level LEVEL_OWN=10.

The permission ReadCompanyPermission would be changed as follows:

public class ReadCompanyPermission extends BasicHierarchyPermission {
 
  public static final int LEVEL_OWN = 10;
 
  private Long m_companyId;
 
  public ReadCompanyPermission(Long companyId) {
    super("ReadCompany" + "." + companyId, LEVEL_UNDEFINED);
    m_companyId = companyId;
  }
 
  protected boolean execCheckLevel(int userLevel) throws ProcessingException {
    if (userLevel == LEVEL_OWN) {
      return SERVICES.getService(ICompanyService.class).isOwnCompany(getCompanyId());
    }
    return false;
  }
 
  public Long getCompanyId() {
    return m_companyId;
  }
}

In order to enable fine-grained access control so the user can only access his personal company objects, the user's permission level of the permission 'ReadCompanyPermission' must be set to 10 when loading the permissions with execLoadPermissions.

The access-check to protect the company resource would be changed as follows:

if (!ACCESS.check(new ReadCompanyPermission(xy))) {
   throw new VetoException("Authorization failed");
} 
else {
   // user is authorized, do some business logic here
}

As you may have noticed, there is provided an implicit level in the ReadCompanyPermission's constructor when doing the super call. This level stands for the minimal required level the user must have in order to access the resource. If you would put LEVEL_ALL in there, the user would not be allowed to access the company resource anymore as his level is only 10 (LEVEL_OWN) which is lower than 100 (LEVEL_ALL). In difference, the level LEVEL_UNDEFINED (-1) does not represent a concrete level, but exclusively stands for fine-grained access control. As a consequence, the access controller does not decide by itself whether to grant access or not. Thereto, it delegates this decision to you by invoking execCheckLevel(int userLevel) on the permission.

In this example, the provided userLevel would be 10 (LEVEL_OWN). That indicates your code that the caller is only allowed to see companies which he belongs to. In consequence, you have to verify the user's relation to the given company and grant access accordingly.

So feel free to define some other fine-grained access levels, e.g. LEVEL_DEPARTMENT = 20;

Please note, that by calling ACCESS.getLevel(Permission p) the user specific access level on that permission can be requested.

Utilities

Codetypes

NLS-Support

to do

Logging

In Eclipse Scout, there exists a transparent way for doing logging. Eclipse Scout supports JUL (using java.util.logging) and the Eclipse Log Framework (using IStatus). Furthermore, a custom log implementation can be registered to use a different Log Framework, f.e. log4j.

Logger Usage

Project code may use IScoutLogger to abstract from the runtime implementation.
private static IScoutLogger logger = ScoutLogManager.getLogger(MyOwnClass.class);

It is possible to add a code template to quickly add this line in you class. Here is the pattern:

${:import(org.eclipse.scout.commons.logger.IScoutLogger,org.eclipse.scout.commons.logger.ScoutLogManager)}
private static final IScoutLogger logger = ScoutLogManager.getLogger(${enclosing_type}.class);

It should appear in the template proposals list (CTRL+Space).


The IScoutLogger-Interface is implemented by EclipseLogWrapper, JavaLogWrapper or CustomLogWrapper which is returned by the above call to ScoutLogManager.getLogger(Class).

Logger Setup

By default, JUL is used. But it is possible to change that behaviour by setting the config.ini property org.eclipse.scout.log or creating a fragment to the host-Plug-In org.eclipse.scout.commons.

JUL

Insert these lines in the config.ini to activate standard java log.

eclipse.consoleLog=false
org.eclipse.scout.log=java
Using different Log Configurations for Development and Productive Environments in JUL

Logging requirements typically differ depending on the target environment.

In development a more detailed logging is desirable and logging to the console is just fine, where for productive use, logging should be directed to a file and should concentrate on errors and warnings.

See this The Scout documentation has been moved to https://eclipsescout.github.io/. for implementation details of this scenario.

Eclipse Log Framework

This is normally used in workbench ui applications with swt.

eclipse.consoleLog=true
org.eclipse.scout.log=eclipse
org.eclipse.scout.log.level=WARNING

Custom Logger (e.g. Log4J)

A custom log implementation can be registered by creating a fragment to the host-Plug-In org.eclipse.scout.commons. In turn, if the config.ini property org.eclipse.scout.log is not set, the ScoutLogManager looks for a class named org.eclipse.scout.commons.logger.CustomLogManager within its classpath. If found, this class is instantiated and used as logging strategy.

As an example the use of log4j is explained in this The Scout documentation has been moved to https://eclipsescout.github.io/.

Additional Logging Features

It is possible to set dynamically a global log level to all loggers no matter of which log level they currently are logging. Of course, this can be made undone and all loggers have their origin log level again.

Furthermore, it is possible to record log messages for a certain amount of time. When recording is stopped, a file is created containing all log messages since recording was started. The combination of the recording functionality together with the change of the log level at runtime facilitates debugging. This is especially true, if systems are to be maintained to easily get some valuable log output.

See this The Scout documentation has been moved to https://eclipsescout.github.io/. for a detailed example of the additional logging features.

Known issues

When using a profiler such as jvisualvm or jconsole with the system property -Dcom.sun.management.jmxremote then the first class loader to load LogManager cannot load the class contained in the logger fragment (even though it is a fragment to the system bundle). This is because the osgi has not even startet yet.

Scout Services

Scout Services

Service Types

Services are managed by the service registry. There is a variety of service types:

  • Data Services
  • Lookup Services
  • Enumeration/Code Services
  • Processing Services
  • Workflow services

A data service is normally a server service providing read-only data with aggregation and composition logic. Data services offer search and filter capabilities. From the client (frontend) such services are called using service remoting over HTTP(S).

A lookup service is normally a server service providing read-only lookup data for dynamic list-of-values such as “Companies”, “Persons”, etc. Lookup Services offer filter and search capabilities and specific data acces “by key”, “by display text”, and “by parent key” (for hierarchical lookup data). From the client these services are called using a lookup service call representing the call data.

An enumeration service is normally a server service providing read-only enumeration data for static list-of-values such as “Project State”, “Address Type”, etc. Note that the word “static” does not mean that the data is fixed and constant, but that the character of the data is rather static. Like Lookup services also code services offer filter and search capabilities. From the client these services are called using a code service call representing the call data.

A processing service is normally a server service providing data manipulation or control operations such as “Company.create”, “Company.modify” etc. From the client these services are called using service remoting over HTTP(S). Most processing services managed by Scout SDK are the backend of UI form models. In order to maximally assist developers, Scout SDK can automatically create a value structure for every UI form that is created and also generate a load/store/create processing service for it.

A workflow service is normally a server service providing state machine and workflow control operations such as “AddressChange.start”, “AddressChange.nextStep” etc. From the client these services are called using service remoting over HTTP(S). Most workflow services managed by Scout SDK are the backend of UI wizard models. In order to maximally assists developers, Scout SDK automatically creates a value structure for every UI wizard that is created and also generates a workflow service for it.

scout.commons

Scheduler

  • The instance for the Job is only created exactly once. Every run of a job is by the same instance.
  • The timer (when to start) is hardcoded (see example).

Usage

  1. Derive a class from AbstractSchedulerJob
  2. implement a constructor calling super(groupId, jobId);
  3. implement / override execAcceptTick
  4. implement / override run
  5. In the ServerApplication you need something like
public class ServerApplication implements IApplication{
  public Object start(IApplicationContext context) throws Exception {
    //start the scheduler
    Scheduler scheduler=new Scheduler(Activator.getDefault().getBackendSubject(),ServerSession.class);
    scheduler.addJob(new LoadJobs());
//    scheduler.addJob(new FetchMailSchedulerJob());
    scheduler.addJob(new LdapSchedulerJob());
    scheduler.addJob(new UpdatePLAOrdersJob());
    scheduler.start();
    Activator.getDefault().setScheduler(scheduler);
    ...

Example:

public class MyJob extends AbstractSchedulerJob {
  private static IScoutLogger s_logger =ScoutLogManager.getLogger(MyJob.class);
  private final static String groupId = "MyGroup";
  private final static String jobId = "MyJob";
  /**
   * <p><code>true</code> the job is currently running, <code>false</code> else</p>
   * <p>Access needs to be guarded / synchronized by <code>this</code>, because it is possible, that the same reference to the job
   * is called twice.</p>.
   */
  private boolean m_running;

  public MyJob() {
    super(groupId, jobId);
  }

  @Override
  protected boolean execAcceptTick(TickSignal signal, int second, int minute, int hour, int day, int week, int month,
      int year, int dayOfWeek, int dayOfMonthReverse, int dayOfYear, int secondOfDay) {
    return (second==0 && minute%10==0); /* start every 10 minutes */
  }

  @Override
  public void run(IScheduler scheduler, TickSignal signal) throws ProcessingException {
    synchronized (this) {
      if (m_running) { /* prevent the job from being started twice */
        s_logger.warn("The Job " + getGroupId() + "." + getJobId() + " is already running, but should be started. Job was not started.");
        return;
      }
      m_running = true;
    }
    try {
      s_logger.info("Started scheduled job: " + getGroupId() + "." + getJobId() + ", process all PLA Orders.");
      IXYZService service = SERVICES.getService(IXYZService.class);
      try {
        service.doStuff();
      } catch (Exception e) {
        s_logger.error("Error in Job " + getGroupId() + "." + getJobId(), e);
      }
      s_logger.info("Finished scheduled job: " + getGroupId() + "." + getJobId() + ", process all PLA Orders");
    } finally {
      synchronized (this) {
        m_running = false;
      }
    }
  }
}

Client Notification

Client Notification Overview

In some circumstances it is necessary to inform the clients about something important. An example could be an incoming call for a specific client or just a request to reload the code type cache. Such requests from server to client are called client notification.

Because Scout does its client-server communication over https it is limited to a one-way connection. That means, the server can’t directly call the client, it can only answer him. Therefore the client asks the server about new information after a certain time period. The server delivers all interesting notifications for this client. The ClientNotificationFilter defines for which client a notification is interesting. So we basically have a queue with pairs of ClientNotifications and ClientNotificationFilters on the server-side and each time a client calls for information, the server iterates over this queue. Of course these pairs only have a limited time to live, which is also decided by the implementation of the ClientNotificationFilter.

The ClientNotificationConsumer will periodically ask the server for new ClientNotifications. There is the possibility to tune the polling interval or the blocking timeout. The polling interval describes after how many milliseconds the ClientNotificationConsumer will do his next polling and the blocking timeout defines how long the call of the ClientNotificationConsumer will wait on the backend and look for new ClientNotifications.

For better performance and lesser HTTP calls, all client notifications that accumulated during a remote service call on the server are collected and attached to the response of the remote service call as additional payload. That means if the client does frequent server calls the client notification poll interval can be decreased.

Because you don’t want to get all available ClientNotifications from the server, a ClientNotification can only be provided together with a ClientNotificationFilter. This filter defines how long a ClientNotification is active, if it’s multicast or not and who is interested in this ClientNotification. It is recommended to define your ClientNotificationFilter on the server side, because then you have access to session relevant information like user number or session id. For trivial cases like the comparison of the user number there exists a bunch of default ClientNotificationFilters.

Scout SDK

Idea Behind Scout SDK

Scout SDK is an Eclipse plugin set which intention is to boost developer productivity in building complete applications. Examples of such applications are:

  • Standalone rich client platforms (Equinox, SWT/Swing)
  • SOA/ESB node consisting of J2EE with service registry and web services (Equinox)
  • Rich client platforms with a J2EE Backend (Equinox, SWT/Swing)

Scout SDK operates on top of both the Eclipse JDT/PDE model. It guides the developer in building Scout based SOA compliant applications. Instead of implementing the same patterns of an application again and again, Scout SDK helps to reduce development time by offering tooling and outline views to navigate the application. As a consequence, developers can focus their work on the business logic of the application. In the background Scout SDK takes care of the sound architecture and complete structure of the Java project. The result of a solution developed with Scout SDK is a pure Java solution consisting of one or more Equinox/Eclipse based applications.

Main Features

Building Forms and Outlines

Generation of DTOs (form data, field data)

NLS Editor

Support for Webservices

Architecture of Scout SDK

Describe how JDT and PDE is used



Back to Scout

Back to the top