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"

(Idea Behind Scout SDK)
Line 1: Line 1:
= Overview  =
+
= Overview  =
  
 
= Architecture  =
 
= Architecture  =
Line 10: Line 10:
  
 
= 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/>
 +
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.
  
 
== Service Tunnel  ==
 
== Service Tunnel  ==
Line 16: Line 21:
  
 
=== 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.
 +
 +
==== Request Message  ====
 +
 +
The request SOAP message consists of:
 +
 +
#Service reference, operation, version, formatting, language
 +
#Service arguments
 +
#Information like timestamp, TCP/IP origin, varia (xsd:any)
 +
 +
<pre>
 +
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>
 +
</pre>
 +
 +
==== Response Message  ====
 +
 +
The response SOAP message consists of:
 +
 +
#Service invocation status, maybe exception type
 +
#Service response data
 +
#Information like timestamp, TCP/IP origin, varia (xsd:any)
 +
 +
Example with Status: „OK“
 +
 +
<pre>
 +
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>
 +
</pre>
 +
 +
Example with Status „ERROR“
 +
 +
<pre>
 +
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>
 +
</pre>
  
 
== Session Handling  ==
 
== Session Handling  ==
Line 47: Line 115:
 
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).  
 
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 ===
+
=== 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.
+
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 ====
+
==== Sorting of Columns ====
# Oracle (or whatever database you prefer) sorts the columns, if an <code>order by</code> clause is specified. Oracle sorts according to the <code>NLS_SORT</code> parameter, which is set per database and can be overwritten in the session.<br><code>select * from V$NLS_PARAMETERS WHERE PARAMETER = 'NLS_SORT';</code> tells you the current setting.
+
 
# If you click on a column (client), Scout does the sort.
+
#Oracle (or whatever database you prefer) sorts the columns, if an <code>order by</code> clause is specified. Oracle sorts according to the <code>NLS_SORT</code> parameter, which is set per database and can be overwritten in the session.<br><code>select * from V$NLS_PARAMETERS WHERE PARAMETER = 'NLS_SORT';</code> tells you the current setting.  
#* StringColumn: Scout orders the entries according the <code>NlsLocale</code> (see <code>java.text.Collator</code>)
+
#If you click on a column (client), Scout does the sort.  
#* SmartColumn:
+
#*StringColumn: Scout orders the entries according the <code>NlsLocale</code> (see <code>java.text.Collator</code>)  
#** If <code>isSortCodesByDisplayText()</code> is set, the sort is done by Java according the <code>NlsLocale</code>.
+
#*SmartColumn:  
#** otherwise the Sortcode is used.
+
#**If <code>isSortCodesByDisplayText()</code> is set, the sort is done by Java according the <code>NlsLocale</code>.  
 +
#**otherwise the Sortcode is used.
  
 
=== Form  ===
 
=== Form  ===
Line 115: Line 184:
 
=== Statement Builder  ===
 
=== Statement Builder  ===
  
== config.ini ==
+
== config.ini ==
  
Inside of the config.ini in the server it is possible to override the member variables of services.
+
Inside of the config.ini in the server it is possible to override the member variables of services.  
  
For example:
+
For example:  
  
com.bsiag.mnet.server.services.common.sql.SqlService#directJ dbcConnection=true
+
com.bsiag.mnet.server.services.common.sql.SqlService#directJ dbcConnection=true  
  
 +
<br> If the service SqlService has a setter method for the member directJdbcConnection then the member has at runtime the value 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
  
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.
  
For other classes it must be done by yourself for example with the class FilterConfigInjection at startup.
+
== Server Side Proxy  ==
  
== 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. <br> In the web you find several sites that tell you to start Java with the following options:  
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. <br>
+
In the web you find several sites that tell you to start Java with the following options:
+
  
 
{{code|
 
{{code|
Line 139: Line 207:
 
-Dhttp.proxyUser&#61;proxyUser <br>
 
-Dhttp.proxyUser&#61;proxyUser <br>
 
-Dhttp.proxyPassword&#61;proxyPassword
 
-Dhttp.proxyPassword&#61;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) <br>
+
(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 &amp; add the four lines to the Java options) <br> When the request is sent, the proxy host and the proxy port are known &amp; 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.  
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.
+
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). <br>
+
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). <br> Your code could look similar to the following code snippet:  
Your code could look similar to the following code snippet:
+
  
 
{{code|
 
{{code|
Line 156: Line 222:
 
String encoded &#61; Base64Utility.encode((myProxyUsername + ":" + myProxyPassword).getBytes()); <br>
 
String encoded &#61; Base64Utility.encode((myProxyUsername + ":" + myProxyPassword).getBytes()); <br>
 
conn.setRequestProperty("Proxy-Authorization", "Basic " + encoded);
 
conn.setRequestProperty("Proxy-Authorization", "Basic " + encoded);
}}
+
}}  
  
However, with this solution you need to set the proxy parameters for each request anew.
+
However, with this solution you need to set the proxy parameters for each request anew.  
  
 
= Security  =
 
= Security  =
  
== Authentication / Authorization ==
+
== Authentication / Authorization ==
  
 +
;Failover Switch
 +
: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
 +
:Identify the user trying to access the system
 +
;Authorization
 +
:Determine the users rights and permissions according to his identity
  
 +
<br>
  
;Failover Switch: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>
+
=== Security Filters  ===
;Authentication:Identify the user trying to access the system
+
;Authorization:Determine the users rights and permissions according to his identity
+
  
 +
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]].
  
 
+
{| border="1" cellpadding="3" cellspacing="0"
=== Security Filters  ===
+
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]].
+
{| border="1" cellpadding="3" cellspacing="0"
+
 
|-
 
|-
! BasicSecurityFilter !! <code>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter</code>
+
! BasicSecurityFilter  
 +
! <code>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter</code>
 
|-
 
|-
| The basic filter with the usernames and passwords directly configured in the configuration file (or the extension point).
+
| 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>
+
</pre>  
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#users=frank\=test,mark\=test,steve\=test
+
</pre>
+
 
|  
 
|  
 
 
|-
 
|-
! DataSourceSecurityFilter !! <code>org.eclipse.scout.rt.server.servlet.filter.DataSourceSecurityFilter</code>
+
! DataSourceSecurityFilter  
 +
! <code>org.eclipse.scout.rt.server.servlet.filter.DataSourceSecurityFilter</code>
 
|-
 
|-
 
| Uses the users and passwords defined in the database.  
 
| Uses the users and passwords defined in the database.  
 
|  
 
|  
 
|}
 
|}
==== Settings in config.ini ====
+
 
For every filter you can set the four options (example with the <code>BasicSecurityFilter</code>).
+
==== Settings in config.ini ====
<pre>
+
 
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/process#active=false
+
For every filter you can set the four options (example with the <code>BasicSecurityFilter</code>).  
 +
<pre>org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/process#active=false
 
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/updatesite#active=false
 
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/updatesite#active=false
 
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/test#active=false
 
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/test#active=false
 
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter/services#active=true
 
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:
+
The settings determine, whether the security filter is used for a given servlet path. The often used paths are:  
;process:This is where all Scout internals are connect, i.e. the calls from the client to the server.
+
;updatesite:Authentication for users connecting to the updatesite (via browser) to download the client. This also includes the automatic updates of the client.
+
;test:Authentication for test servlets
+
;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>
+
;process
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#failover=true
+
:This is where all Scout internals are connect, i.e. the calls from the client to the server.
</pre>
+
;updatesite
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>).
+
:Authentication for users connecting to the updatesite (via browser) to download the client. This also includes the automatic updates of the client.
 +
;test
 +
:Authentication for test servlets
 +
;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>  
 +
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 ===
+
=== Example Setups ===
  
 
== Granting  ==
 
== Granting  ==
Line 222: Line 294:
  
 
== NLS-Support  ==
 
== NLS-Support  ==
to do
 
  
== Logging ==
+
to do
Scout has moved to [http://www.slf4j.org slf4j]. slf4j is a logging facade, which is implemented by [[#Logger Implementations|various loggers]].
+
  
slf4j also offers bridges that map calls to the "old" loggers to slf4j.
+
== Logging  ==
  
The development environment contains just Logback, impl.simple, impl.nop and the bridges.
+
Scout has moved to [http://www.slf4j.org slf4j]. slf4j is a logging facade, which is implemented by [[#Logger_Implementations|various loggers]].  
  
=== Logger Usage ===
+
slf4j also offers bridges that map calls to the "old" loggers to slf4j.  
Scout code should only use the <code>IScoutLogger</code>.
+
<pre>
+
  private static IScoutLogger logger = ScoutLogManager.getLogger(MyOwnClass.class);
+
</pre>
+
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.
+
  
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]]).
+
The development environment contains just Logback, impl.simple, impl.nop and the bridges.  
  
Example:
+
=== Logger Usage  ===
<pre>
+
 
### Logging
+
Scout code should only use the <code>IScoutLogger</code>.
 +
<pre>  private static IScoutLogger logger = ScoutLogManager.getLogger(MyOwnClass.class);
 +
</pre>
 +
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.
 +
 
 +
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]]).
 +
 
 +
Example:  
 +
<pre>### Logging
 
# sets the default loglevel, -1=INHERITED (definitions from logback-test.xml),
 
# 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
 
#  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
 
org.eclipse.scout.commons.logger.level=2
</pre>
+
</pre>  
 +
=== Logger Implementations  ===
  
=== Logger Implementations ===
+
;Logback
;Logback:The native implementation and a successor to log4j
+
:The native implementation and a successor to log4j  
;impl.simple:Redirection of slf4j calls to std-out
+
;impl.simple
;impl.Log4j:Redirection of slf4j calls to log4j
+
:Redirection of slf4j calls to std-out  
;impl.jakarta.commons.logging: Redirection slf4j calls to jcl
+
;impl.Log4j
;impl.Java.util.logging: Redirection of slf4j calls to jul
+
:Redirection of slf4j calls to log4j  
;impl.nop: Redirection of slf4j calls to "nothing"
+
;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 ===
+
=== Using slf4j/Logback ===
To use slf4j/Logback the following fragments / plugings are required. Include them as dependencies in your product-file.
+
  
* ch.qos.logback.core
+
To use slf4j/Logback the following fragments / plugings are required. Include them as dependencies in your product-file.  
* ch.qos.logback.slf4j
+
* 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:
+
*ch.qos.logback.core
* org.slf4j.impl.nop
+
*ch.qos.logback.slf4j
* org.slf4j.impl.siml
+
*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>
  
Also do '''not''' include fragments implementing log4j or any other logger (these calls will be intercepted by org.slf4j.log4j and forwarded to slf4j).
+
Make sure the following dependencies have NOT been included:
  
==== <code>fragment.name.org</code> ====
+
*org.slf4j.impl.nop
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.
+
*org.slf4j.impl.siml
  
<code>MANIFEST.MF</code>:
+
Also do '''not''' include fragments implementing log4j or any other logger (these calls will be intercepted by org.slf4j.log4j and forwarded to slf4j).
<pre>
+
 
Manifest-Version: 1.0
+
==== <code>fragment.name.org</code>  ====
 +
 
 +
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.
 +
 
 +
<code>MANIFEST.MF</code>:  
 +
<pre>Manifest-Version: 1.0
 
Bundle-ManifestVersion: 2
 
Bundle-ManifestVersion: 2
 
Bundle-Name: Logback Fragment
 
Bundle-Name: Logback Fragment
Line 287: Line 368:
 
Fragment-Host: org.slf4j.api;bundle-version="1.6.0"
 
Fragment-Host: org.slf4j.api;bundle-version="1.6.0"
 
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
 
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
</pre>
+
</pre>  
 +
<code>logback-test.xml</code>:
 +
<pre>&lt;configuration scan="true"&gt;
 +
  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
 +
    &lt;!-- encoders are assigned the type
 +
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;
  
<code>logback-test.xml</code>:
+
  &lt;logger name="org.eclipse.scout.commons.ConfigIniUtility"&gt;
<pre>
+
    &lt;level value="WARN" /&gt;
<configuration scan="true">
+
  &lt;/logger&gt;
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+
  &lt;root&gt;
    <!-- encoders are assigned the type
+
    &lt;level value="WARN" /&gt;
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+
    &lt;appender-ref ref="CONSOLE" /&gt;
    <encoder>
+
  &lt;/root&gt;
      <pattern>%d{ISO8601} %-5level [%thread] %logger: %msg%n</pattern>
+
&lt;/configuration&gt;
    </encoder>
+
</pre>
  </appender>
+
<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;
  
  <logger name="org.eclipse.scout.commons.ConfigIniUtility">
+
&lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;
    <level value="WARN" />
+
&lt;!-- rollover daily --&gt;
  </logger>
+
&lt;fileNamePattern&gt;c:/temp/project-%d{yyyy-MM-dd}.%i.log&lt;/fileNamePattern&gt;
  <root>
+
&lt;timeBasedFileNamingAndTriggeringPolicy
    <level value="WARN" />
+
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"&gt;
    <appender-ref ref="CONSOLE" />
+
&lt;!-- or whenever the file size reaches 100MB --&gt;
  </root>
+
&lt;maxFileSize&gt;100MB&lt;/maxFileSize&gt;
</configuration>
+
&lt;/timeBasedFileNamingAndTriggeringPolicy&gt;
</pre>
+
  
<code>logback.xml</code>:
+
&lt;!-- keep 30 days' worth of history --&gt;
<pre>
+
&lt;maxHistory&gt;30&lt;/maxHistory&gt;
<configuration scan="true">
+
&lt;/rollingPolicy&gt;
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+
<file>c:/temp/project.log</file>
+
  
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+
&lt;!-- encoders are assigned the type
<!-- rollover daily -->
+
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
<fileNamePattern>c:/temp/project-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+
&lt;encoder&gt;
<timeBasedFileNamingAndTriggeringPolicy
+
&lt;pattern&gt;%d{ISO8601}&nbsp;%-5level [%thread]&nbsp;%logger:&nbsp;%msg%n&lt;/pattern&gt;
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+
&lt;/encoder&gt;
<!-- or whenever the file size reaches 100MB -->
+
&lt;/appender&gt;
<maxFileSize>100MB</maxFileSize>
+
</timeBasedFileNamingAndTriggeringPolicy>
+
  
<!-- keep 30 days' worth of history -->
+
&lt;logger name="org.eclipse.scout.commons.ConfigIniUtility"&gt;
<maxHistory>30</maxHistory>
+
&lt;level value="WARN" /&gt;
</rollingPolicy>
+
&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;
  
<!-- encoders are assigned the type
+
&lt;root&gt;
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+
&lt;level value="INFO" /&gt;
<encoder>
+
&lt;appender-ref ref="FILE" /&gt;
<pattern>%d{ISO8601} %-5level [%thread] %logger: %msg%n</pattern>
+
&lt;/root&gt;
</encoder>
+
&lt;/configuration&gt;
</appender>
+
</pre>  
 
+
<code>build.properties</code>  
<logger name="org.eclipse.scout.commons.ConfigIniUtility">
+
<pre>bin.includes = META-INF/,\
<level value="WARN" />
+
</logger>
+
<logger name="org.eclipse.scout.rt.server.services.common.sql">
+
<level value="INFO" />
+
</logger>
+
<logger name="org.eclipse.scout.rt.ui.swing.SwingIconLocator">
+
<level value="WARN" />
+
</logger>
+
 
+
<root>
+
<level value="INFO" />
+
<appender-ref ref="FILE" />
+
</root>
+
</configuration>
+
</pre>
+
 
+
<code>build.properties</code>
+
<pre>
+
bin.includes = META-INF/,\
+
 
               licence.html,\
 
               licence.html,\
 
               logback.xml
 
               logback.xml
</pre>
+
</pre>  
 +
=== Logback Configuration  ===
  
=== Logback Configuration ===
+
==== Different Configuration for Production and Development  ====
  
==== Different Configuration for Production and Development ====
+
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.  
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.
+
  
If you start the Scout application from Eclipse, the <code>logback-test.xml</code> takes precedence.
+
If you start the Scout application from Eclipse, the <code>logback-test.xml</code> takes precedence.  
  
And for the deployment there is only the <code>logback.xml</code>, so nothing to worry about either.
+
And for the deployment there is only the <code>logback.xml</code>, so nothing to worry about either.  
  
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.
+
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.  
  
==== Automatically Rescanning the Configuration ====
+
==== Automatically Rescanning the Configuration ====
The automatic scanning for configuration changes is enabled by the attribute <code>scan="true"</code> in the configuration xml.
+
<pre>
+
  <configuration scan="true">
+
</pre>
+
  
 +
The automatic scanning for configuration changes is enabled by the attribute <code>scan="true"</code> in the configuration xml.
 +
<pre>  &lt;configuration scan="true"&gt;
 +
</pre>
 +
<br>
  
* TODO: Where does it look for the configuration?
+
*TODO: Where does it look for the configuration?
  
 
== Scout Services  ==
 
== Scout Services  ==
Line 387: Line 462:
  
 
== Scheduler  ==
 
== 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 ===
+
*The instance for the Job is only created exactly once. Every run of a job is by the same instance.
# Derive a class from <code>AbstractSchedulerJob</code>
+
*The timer (when to start) is hardcoded (see example).
# implement a constructor calling <code>super(groupId, jobId);</code>
+
 
# implement / override <code>execAcceptTick</code>
+
=== Usage ===
# implement / override <code>run</code>
+
 
# In the ServerApplication you need something like
+
#Derive a class from <code>AbstractSchedulerJob</code>  
<pre>
+
#implement a constructor calling <code>super(groupId, jobId);</code>  
public class ServerApplication implements IApplication{
+
#implement / override <code>execAcceptTick</code>  
 +
#implement / override <code>run</code>  
 +
#In the ServerApplication you need something like
 +
<pre>public class ServerApplication implements IApplication{
 
   public Object start(IApplicationContext context) throws Exception {
 
   public Object start(IApplicationContext context) throws Exception {
 
     //start the scheduler
 
     //start the scheduler
Line 408: Line 484:
 
     Activator.getDefault().setScheduler(scheduler);
 
     Activator.getDefault().setScheduler(scheduler);
 
     ...
 
     ...
</pre>
+
</pre>  
 
+
Example:  
Example:
+
<pre>public class MyJob extends AbstractSchedulerJob {
<pre>
+
public class MyJob extends AbstractSchedulerJob {
+
 
   private static IScoutLogger s_logger =ScoutLogManager.getLogger(MyJob.class);
 
   private static IScoutLogger s_logger =ScoutLogManager.getLogger(MyJob.class);
 
   private final static String groupId = "MyGroup";
 
   private final static String groupId = "MyGroup";
 
   private final static String jobId = "MyJob";
 
   private final static String jobId = "MyJob";
 
   /**
 
   /**
   * <p><code>true</code> the job is currently running, <code>false</code> else</p>
+
   * &lt;p&gt;&lt;code&gt;true&lt;/code&gt; the job is currently running, &lt;code&gt;false&lt;/code&gt; else&lt;/p&gt;
   * <p>Access needs to be guarded / synchronized by <code>this</code>, because it is possible, that the same reference to the job
+
   * &lt;p&gt;Access needs to be guarded / synchronized by &lt;code&gt;this&lt;/code&gt;, because it is possible, that the same reference to the job
   * is called twice.</p>.
+
   * is called twice.&lt;/p&gt;.
 
   */
 
   */
 
   private boolean m_running;
 
   private boolean m_running;
Line 430: Line 504:
 
   protected boolean execAcceptTick(TickSignal signal, int second, int minute, int hour, int day, int week, int month,
 
   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) {
 
       int year, int dayOfWeek, int dayOfMonthReverse, int dayOfYear, int secondOfDay) {
     return (second==0 && minute%10==0); /* start every 10 minutes */
+
     return (second==0 &amp;&amp; minute%10==0); /* start every 10 minutes */
 
   }
 
   }
  
Line 458: Line 532:
 
   }
 
   }
 
}
 
}
</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.
 +
 +
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.
 +
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]]
  
 
= Scout SDK  =
 
= Scout SDK  =
  
 
== Idea Behind 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)
+
Scout SDK is an Eclipse plugin set which intention is to boost developer productivity in building complete applications. Examples of such applications are:  
*SOA/ESB node consisting of J2EE with service registry and web services (Equinox)
+
 
 +
*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)
 
*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.
+
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.  
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  ==
 
== Main Features  ==

Revision as of 17:43, 14 November 2010

Overview

Architecture

Scout architecture overview.png

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.

Service Tunnel

Proxy services, Service Factories Entry point server (servlet) Outgoing point client

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 ... 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

Separation of UI and GUI

Scout gui representation.png

Job Queue

Component Model

Client components.png

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 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

Configuration

SQL Support

Statement Builder

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

Authentication / Authorization

Failover Switch
Determines whether the next filter should be used to try to authenticate / authorize the user.
The last filter has to set this to false
Authentication
Identify the user trying to access the system
Authorization
Determine the users rights and permissions according to his identity


Security Filters

The security filters need to be included as extensions in the server's plugin.xml as extensions. The configuration of these extensions points works as defaults, but is overriden by the #Settings_in_config.ini.

BasicSecurityFilter org.eclipse.scout.rt.server.servlet.filter.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 org.eclipse.scout.rt.server.servlet.filter.DataSourceSecurityFilter
Uses the users and passwords defined in the database.

Settings in config.ini

For every filter you can set the four options (example with the BasicSecurityFilter).

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

The settings determine, whether the security filter is used for a given servlet path. The often used paths are:

process
This is where all Scout internals are connect, i.e. the calls from the client to the server.
updatesite
Authentication for users connecting to the updatesite (via browser) to download the client. This also includes the automatic updates of the client.
test
Authentication for test servlets
services
Authentication for webservices, this will probably always be the BasicSecurityFilter as the other side's implementation has the credentials configured somewhere as well.
org.eclipse.scout.rt.server.servlet.filter.BasicSecurityFilter#failover=true

Determines, whether the filter should deny access, if not successfully authenticated (false) or if the next filter should try to authenticate (true).

Example Setups

Granting

Describe the Permisssion Classes, the permission store, ACCESS....

Utilities

Codetypes

NLS-Support

to do

Logging

Scout has moved to slf4j. slf4j is a logging facade, which is implemented by various loggers.

slf4j also offers bridges that map calls to the "old" loggers to slf4j.

The development environment contains just Logback, impl.simple, impl.nop and the bridges.

Logger Usage

Scout code should only use the IScoutLogger.

  private static IScoutLogger logger = ScoutLogManager.getLogger(MyOwnClass.class);

The IScoutLogger-Interface is implemented by SLF4JDelegateLogger, which is returned by the above call to ScoutLogManager.getLogger(Class). This wrapps one of the various possible log implementations.

In your config.ini you can set the property org.eclipse.scout.commons.logger.level. 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 logback.xml (see Using slf4j/Logback).

Example:

### Logging
# 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

Logger Implementations

Logback
The native implementation and a successor to log4j
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

To use slf4j/Logback the following fragments / plugings are required. Include them as dependencies in your product-file.

  • ch.qos.logback.core
  • ch.qos.logback.slf4j
  • org.slf4j.api
  • org.slf4j.ext
  • org.slf4j.jcl (optional)
  • org.slf4j.jul (optional)
  • org.slf4j.log4j (optional)
  • a dedicated fragment: fragment.name.org

Make sure the following dependencies have NOT been included:

  • org.slf4j.impl.nop
  • org.slf4j.impl.siml

Also do not include fragments implementing log4j or any other logger (these calls will be intercepted by org.slf4j.log4j and forwarded to slf4j).

fragment.name.org

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.

MANIFEST.MF:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Logback Fragment
Bundle-SymbolicName: fragment.name.org
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: ....
Fragment-Host: org.slf4j.api;bundle-version="1.6.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6

logback-test.xml:

<configuration scan="true">
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
				ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%d{ISO8601} %-5level [%thread] %logger: %msg%n</pattern>
    </encoder>
  </appender>

  <logger name="org.eclipse.scout.commons.ConfigIniUtility">
    <level value="WARN" />
  </logger>
  <root>
    <level value="WARN" />
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>

logback.xml:

<configuration scan="true">
	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>c:/temp/project.log</file>

		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
			<!-- rollover daily -->
			<fileNamePattern>c:/temp/project-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
			<timeBasedFileNamingAndTriggeringPolicy
						class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
				<!-- or whenever the file size reaches 100MB -->
				<maxFileSize>100MB</maxFileSize>
			</timeBasedFileNamingAndTriggeringPolicy>

			<!-- keep 30 days' worth of history -->
			<maxHistory>30</maxHistory>
		</rollingPolicy>

		<!-- encoders are assigned the type
				ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
		<encoder>
			<pattern>%d{ISO8601} %-5level [%thread] %logger: %msg%n</pattern>
		</encoder>
	</appender>

	<logger name="org.eclipse.scout.commons.ConfigIniUtility">
		<level value="WARN" />
	</logger>
	<logger name="org.eclipse.scout.rt.server.services.common.sql">
		<level value="INFO" />
	</logger>
	<logger name="org.eclipse.scout.rt.ui.swing.SwingIconLocator">
		<level value="WARN" />
	</logger>

	<root>
		<level value="INFO" />
		<appender-ref ref="FILE" />
	</root>
</configuration>

build.properties

bin.includes = META-INF/,\
               licence.html,\
               logback.xml

Logback Configuration

Different Configuration for Production and Development

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 logback.xml for production / deployment and a logback-test.xml for the development (both in the fragment fragment.name.org). Then you edit the build.properties so the logback-test.xml is not included in the build.

If you start the Scout application from Eclipse, the logback-test.xml takes precedence.

And for the deployment there is only the logback.xml, so nothing to worry about either.

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.

Automatically Rescanning the Configuration

The automatic scanning for configuration changes is enabled by the attribute scan="true" in the configuration xml.

  <configuration scan="true">


  • TODO: Where does it look for the configuration?

Scout Services

Scout services.png

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

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.

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.

ClientNotificationOverview.png

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

Copyright © Eclipse Foundation, Inc. All Rights Reserved.