January 5, 2015

JavaMail example with Zoho/GMail using SMTPS

Here is a simple example of how to send emails securely using JavaMail via your Gmail or Zoho account.

The first example uses TLS, which should be your preferred encryption mechanism. The second example uses SSL.

The only difference between the two mechanisms is the port number, and the mail.smtp.startXXX.enable property.

For SSL use:

port 465
mail.smtp.startssl.enable

For TLS use:

port 987
mail.smtp.starttls.enable
 
Get the source on GitHub here

Example 1: Zoho/Gmail with TLS

package com.ritchie.email;

import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class SendEmailService {

    private Logger logger = Logger.getLogger(SendEmailService.class.getName());

    public void sendEmail() {

        final String GMAIL_HOST = "smtp.gmail.com";
        final String ZOHO_HOST = "smtp.zoho.com";
        final String TLS_PORT = "897";

        final String SENDER_EMAIL = "username@zoho.com";
        final String SENDER_USERNAME = "username@zoho.com";
        final String SENDER_PASSWORD = "zoho-password";

        // protocol properties
        Properties props = System.getProperties();
        props.setProperty("mail.smtps.host", ZOHO_HOST); // change to GMAIL_HOST for gmail                                                         // for gmail
        props.setProperty("mail.smtp.port", TLS_PORT);
        props.setProperty("mail.smtp.starttls.enable", "true");
        props.setProperty("mail.smtps.auth", "true");
        // close connection upon quit being sent
        props.put("mail.smtps.quitwait", "false");

        Session session = Session.getInstance(props, null);

        try {
            // create the message
            final MimeMessage msg = new MimeMessage(session);

            // set recipients and content
            msg.setFrom(new InternetAddress(SENDER_EMAIL));
            msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("recipient@gmail.com", false));
            msg.setSubject("Demo");
            msg.setText("Message Sent via JavaMail", "utf-8", "html");
            msg.setSentDate(new Date());

            // this means you do not need socketFactory properties
            Transport transport = session.getTransport("smtps");

            // send the mail
            transport.connect(ZOHO_HOST, SENDER_USERNAME, SENDER_PASSWORD);
            transport.sendMessage(msg, msg.getAllRecipients());
            transport.close();

        } catch (MessagingException e) {
            logger.log(Level.SEVERE, "Failed to send message", e);

        }
    }
}

Example 2: Zoho/Gmail with SSL

package com.ritchie.email;

import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class SendEmailService {

    private Logger logger = Logger.getLogger(SendEmailService.class.getName());

    public void sendEmail() {

        final String GMAIL_HOST = "smtp.gmail.com";
        final String ZOHO_HOST = "smtp.zoho.com";
        final String SSL_PORT = "465";

        final String SENDER_EMAIL = "username@zoho.com";
        final String SENDER_USERNAME = "username@zoho.com";
        final String SENDER_PASSWORD = "zoho-password";

        // protocol properties
        Properties props = System.getProperties();
        props.setProperty("mail.smtps.host", ZOHO_HOST); // change to GMAIL_HOST for gmail                                                         // for gmail
        props.setProperty("mail.smtp.port", SSL_PORT);
        props.setProperty("mail.smtp.startssl.enable", "true");
        props.setProperty("mail.smtps.auth", "true");
        // close connection upon quit being sent
        props.put("mail.smtps.quitwait", "false");

        Session session = Session.getInstance(props, null);

        try {
            // create the message
            final MimeMessage msg = new MimeMessage(session);

            // set recipients and content
            msg.setFrom(new InternetAddress(SENDER_EMAIL));
            msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("recipient@gmail.com", false));
            msg.setSubject("Demo");
            msg.setText("Message Sent via JavaMail", "utf-8", "html");
            msg.setSentDate(new Date());

            // this means you do not need socketFactory properties
            Transport transport = session.getTransport("smtps");

            // send the mail
            transport.connect(ZOHO_HOST, SENDER_USERNAME, SENDER_PASSWORD);
            transport.sendMessage(msg, msg.getAllRecipients());
            transport.close();

        } catch (MessagingException e) {
            logger.log(Level.SEVERE, "Failed to send message", e);

        }
    }
} 


January 3, 2015

Installing Meld on Mac OS using macports

After playing around with various merge tools on Mac, I have come to the conclusion that none match the ease of use that Meld offers. Here is how I managed to install it on my Mac OS X 10.8.5.
  1. First install macports
  2. If you are running Mac OS 10.8 and greater, you will need to install xquartz which provides the X Window system required for Meld. If you have Mac OS prior to 10.8 you can skip this step.
  3. Install Meld and its dependencies:
    sudo port install rarian
    sudo port install meld
    
  4. Set your locale (I hard coded this so I do not have to export it each time) by updating the /opt/local/bin/meld script by changing line 75 from:
    locale.setlocale(locale.LC_ALL,'')
    to
    locale.setlocale(locale.LC_ALL,'en_US')
  5. Start the service at boot
    launchctl load -w /Library/LaunchAgents/org.freedesktop.dbus-session.plist
  6. Finally run meld using spotlight or via the command line: /opt/local/bin/meld

 

Notes: 

If you try to run Meld without having xquartz installed then you will get the following error:
(process:65163): Gtk-WARNING **: Locale not supported by C library.
 Using the fallback 'C' locale.
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/gtk-2.0/gtk/__init__.py:57: GtkWarning: could not open display
  warnings.warn(str(e), _gtk.Warning)
/opt/local/bin/meld:126: GtkWarning: GtkIconTheme *gtk_icon_theme_get_for_screen(GdkScreen *): assertion 'GDK_IS_SCREEN (screen)' failed
  gtk.icon_theme_get_default().append_search_path(meld.paths.icon_dir())
Traceback (most recent call last):
  File "/opt/local/bin/meld", line 126, in 
    gtk.icon_theme_get_default().append_search_path(meld.paths.icon_dir())
AttributeError: 'NoneType' object has no attribute 'append_search_path'
logout 
 

Reference:

http://support.apple.com/en-us/HT201341
http://thebugfreeblog.blogspot.com/2014/03/installing-meld-on-mac-os-x.html

September 29, 2014

LocalDate java 8 Custom Serializer Jackson JSON Example

In this previous example we use serialize and deserialize classes provided by a Jackson third party datatype. For more control over the date formatting you can opt to create your own serialize and deserialize classes.

WildFly 8.1.0 uses Jackson 2.3.2 so we can add the following dependencies to the pom with a scope of provided:
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.4.2</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.4.2</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.4.2</version>
    <scope>provided</scope>
</dependency>
Then on the LocalDate property of your bean add the following annotations:
public class MyBean {

    @JsonDeserialize(using = JsonDateDeserializer.class)
    @JsonSerialize(using = JsonDateSerializer.class)
    private LocalDate date;
 
    public LocalDate getDate() {
        return date;
    }

    private void setDate(LocalDate date) {
        this.date = date;
    }
} 
Now create the custom JsonDateSerializer:
public class JsonDateSerializer extends JsonSerializer<LocalDate> {

    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
 
    @Override
    public void serialize(LocalDate date, JsonGenerator generator,
            SerializerProvider provider) throws IOException,
            JsonProcessingException {

        String dateString = date.format(formatter);
        generator.writeString(dateString);
    }
}
And now the custom JsonDateDeserializer:
public class JsonDateDeserializer extends JsonDeserializer<LocalDate> {

    @Override
    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        ObjectCodec oc = jp.getCodec();
        TextNode node = (TextNode) oc.readTree(jp);
        String dateString = node.textValue();

        Instant instant = Instant.parse(dateString);
        LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        LocalDate date = LocalDate.of(dateTime.getYear(), dateTime.getMonth(), dateTime.getDayOfMonth());
        return date;
    }
}

NOTE: The package names have changed in Jackson 2. When you use the Jackson annotations make sure you import the one from the com.fasterxml.jackson.databind.annotation.* package and not the ones from the org.codehaus.jackson.map.annotate.* package. If you import the latter, your annotations will be ignored. WildFly 8 does not load the classes from the 1.9.2 jackson module.

September 23, 2014

Java 8 LocalDate with Jackson Serialize and Deserialize Example - RESTful WS

Although I having using Joda Time for years, it is now time to migrate over to the Java 8 Date and Time API (JSR310), and make use of the new LocalDate and LocalTime classes.

WildFly 8.1.0 uses Jackson 2.3.2 which does not know how to (de)serialize the JSR310 Date Time classes. So in order to use the Date and Time API we need to add a Jackson third party datatype dependency to our pom:
<dependency>
    <groupid>com.fasterxml.jackson.datatype</groupid>
    <artifactid>jackson-datatype-jsr310</artifactid>
    <version>2.4.0</version>
</dependency>
Then to deserialize your LocalDate add the @JsonDeserialize annotation and use the LocalDateDeserializer:
@Path("/resource")
@Produces("application/json")
public class MyRestResource {

    @GET
    @Path("now")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    public LocalDate get() {
        return LocalDate.now();
    }
}
To serialize your LocalDate add @JsonSerialize annotation and use the LocalDateSerializer:
@JsonSerialize(using = LocalDateSerializer.class)

To create your own serializer/deserializer check this example, which shows you how you can format the date.

July 17, 2014

Save Session State Between Redeploys - Developing on WildFly

When developing, it is often convenient to save session state between redeploys or server restarts. To enable this feature in WildFly 8 you need to add the persistent-sessions element to your configuration file (within the undertow subsystem).

<servlet-container name="default">
    <persistent-sessions path="session" relative-to="jboss.server.temp.dir"/>
    <jsp-config/>
</servlet-container>
By specifying the relative-to attribute, the session will only be persistent across redeploys and not across server restarts.

This feature works in WildFly 8.2.0.CR1 and greater.

You can also achieve session passivation, when you are using a non-ha profile, by adding <distributable/> to your web.xml (for those using WildFly 8.1.0 or less).

April 22, 2014

How to use Java 8 with WildFly 8 and Eclipse

Prerequisites:

1. Download and install Java 8
2. Download and install WildFly 8
4. Download and install Eclipse Luna (4.4) (or Kepler 4.3 patched for Java 8)

Configure WildFlys VM args

Before starting WildFly, update your standalone.conf, or domain.conf by replacing the following JVM argument:
-XX:MaxPermSize=256m
with:
-XX:MaxMetaspaceSize=256m

The removal of the MaxPermSize property will prevent VM warnings from showing in WildFlys startup logs.

The -XX:PermSize and -XX:MaxPermSize VM arguments are now redundant as PermGen has been replaced in Java 8 with Metaspace. The new VM arguments are -XX:MetaspaceSize and -XX:MaxMetaspaceSize.
Note that the Metaspace data will sit in your computers native memory. For a rundown on the differences between PermGen and Metaspace, check out this blog.

Installing JBoss Tools

In Eclipse Luna (4.4), go to Help > Eclipse Marketplace and type 'JBoss Tools', and click go.  Make sure that you select and install the version that matches your Eclipse install. In this example it is JBoss Tools (Luna) 4.2.0.Beta1.



Click install.

On the next screen you can select which components of JBoss Tools you want to install. To install just the server adapter, select 'JBossAS Tools'. Agree to the terms and click OK. Restart Eclipse when you are given the option to do so.

Now choose File > New > Server. Expand the 'JBoss Community' node and select the option 'WildFly 8'.



Click Next, and Next again

Make sure you select your installed Java 8 JRE, and that you point the home directory to that of your WildFly root directory (As shown below). Click finish.



Finally, in your servers view (Window > Show View > Servers), Select the WildFly Server, right click and select start. All being well, WildFly should now start up.


Finally to configure your WildFly server settings, double click on the WildFly server in your servers view. The screen above should be displayed, allowing you to configure your server/deployment properties.

October 17, 2013

WildFly Server EE7 BOM Maven Coordinates

By adding the Java EE7 BOM (bill of materials) to your pom.xml prevents you from having to add multiple dependencies to your Maven projects. With this you can add a single dependency and have access to all the Java EE7 API's. Here is how:

First add the JBoss repository to your maven pom.xml

<repositories>
    <repository>
        <id>JBoss Repository</id>
        <url>https://repository.jboss.org/nexus/content/groups/public/</url>
    </repository>
</repositories>

Now simply add the BOM, a single dependency:

<dependencies>
    <dependency>
        <groupId>org.jboss.spec</groupId>
        <artifactId>jboss-javaee-7.0</artifactId>
        <version>1.0.0.Final</version>
        <type>pom</type>
    </dependency>
</dependencies>

And thats it!

If your only interested the BOM for the web profile then change the artifactId to jboss-javaee-web-7.0. Likewise, if if you want the entire EE7 API then use jboss-javaee-all-7.0.

October 10, 2013

Simple Managed Thread Factory Example in Wildfly

In this example we are going to use the ManagedThreadFactory (part of Java EE7 Concurrency Utils) to create and run a background task. The advantage of this, rather than using java.lang.Thread, is that your new thread will have access to the other Java EE services. Using the ManagedThreadFactory ensures your threads are created, and managed, by the container.

Example

Like previous examples, we need a Runnable task which will define what work is to be done in the background.

public class ReportTask implements Runnable {

    Logger logger = Logger.getLogger(getClass().getSimpleName());

    public void run() {
        logger.log(Level.INFO, "New thread running...");
        try {
            //do your background task
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            logger.log(Level.SEVERE, "Thread interrupted", e);
        }
    }
}

To get a container managed thread, we simply ask the ManagedThreadFactory for a new thread, and pass it our Runnable instance.  To start the thread we call start().

@Stateless
public class ReportBean {
 
    @Resource(lookup="java:jboss/ee/concurrency/factory/MyManagedThreadFactory")
    private ManagedThreadFactory threadFactory;

    public void runReports() {
        ReportTask reportTask = new ReportTask();
        Thread thread = threadFactory.newThread(reportTask);
        thread.start();
    }
}

We now need to configure our ManagedThreadFactory within Wildfly. This snippet is taken from the ee subsystem configuration in standalone.xml. The 'lookup' attribute of the @Resource annotation maps to 'jndi-name' attribute within the <managed-thread-factory> tag.

<subsystem xmlns="urn:jboss:domain:ee:2.0">
    ...
    <managed-thread-factories>
        <managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
        <managed-thread-factory name="myManagedThreadFactory" jndi-name="java:jboss/ee/concurrency/factory/MyManagedThreadFactory" context-service="default" priority="1"/>
    </managed-thread-factories>
    ...
</subsystem>

Below is the full list of attributes you can use to configure your managed-thread-factory in Wildfly.
  • name - This is the name of the resource. This attribute is manditory.
  • jndi-name - The JNDI name for this resource. This attribute is manditory
  • context-service - The context service to use, if not supplied, the default context service is used.
  • priority - The priority that the thread should have, from 1 to 10 inclusive. Default is 5. 

Using the Command Line Interface (CLI)

To create your ManagedThreadFactory using CLI, go to your terminal and connect to your server by running the following command, swapping out wildfly for your WildFly root directory:
$ wildfly/bin/jboss-cli.sh --connect
Now create the ManagedThreadFactory and set its properties by using the add command, passing through the required property-value pairs.
[standalone@localhost:9990 /] /subsystem=ee/managed-thread-factory=myManagedThreadFactory:add(priority=1,context-service=default,jndi-name=java:jboss/ee/concurrency/factory/MyManagedThreadFactory)
If you want to modify any of these properties use the write-attribute command, passing though the name of the attribute and the new value:
[standalone@localhost:9990 /] /subsystem=ee/managed-thread-factory=myManagedThreadFactory:write-attribute(name=priority,value=3)
Finally reload the server to make your changes avaliable:
[standalone@localhost:9990 /] /:reload

The Admin Console

To view your thread factory in the Wildfly Admin console, navigate to Runtime > JNDI View > java:jboss > ee > concurrent.  Here, you can see all the concurrent resources including your newly configured resource.


Note: Using the JNDI prefix of java:jboss/ee/concurrency/factory ensures that the resource shows under concurrency/factory list in the Wildfly admin console.


September 26, 2013

How to Configure Scheduled Tasks in Wildfly

This example shows you how to create a scheduled concurrent task that runs every hour, using the Concurrency Utils API, and how to configure the properties of a ManagedScheduledExecutorService within Wildfly.

Example


Firstly, lets create the Runnable task which will define what work is to be done in the background thread.

public class ReportTask implements Runnable {
 
    public void run() {
        try {
            //do your background task
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            //handle execption
        }
    }
}

Now we schedule the executor to run its first execution immediately, and every hour thereafter. This executor will continue to run until you either terminate it, or until an exception it thrown by the task. We call the scheduledAtFixedRate() method, passing through our runnable task, along with the properties defining our schedule, i.e. the delay, the frequency and time unit.

@Stateless
public class ReportBean {
 
    @Resource(lookup="java:jboss/ee/concurrency/scheduler/MyScheduledExectutorService")
    private ManagedScheduledExecutorService executorService;

    public void runReports() {
        ReportTask reportTask = new ReportTask();
        executorService.scheduleAtFixedRate(reportTask, 0L, 1L, TimeUnit.HOURS);
    }
}

Lastly we need to configure our ManagedScheduledExecutorService in Wildfly.  The 'lookup' attribute of the @Resource annotation maps to 'jndi-name' attribute within the <managed-scheduled-executor-service> tag.

<subsystem xmlns="urn:jboss:domain:ee:2.0">
            <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
            <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>
            <concurrent>
                <context-services>
                    <context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
                </context-services>
                <managed-thread-factories>
                    <managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
                </managed-thread-factories>
                <managed-executor-services>
                    <managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" core-threads="5" max-threads="25" keepalive-time="5000"/>
                </managed-executor-services>
                <managed-scheduled-executor-services>
                    <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" core-threads="2" keepalive-time="3000"/>
                    <managed-scheduled-executor-service name="myScheduledExecutorService" jndi-name="java:jboss/ee/concurrency/scheduler/MyScheduledExectutorService" hung-task-threshold="50000" core-threads="4" keepalive-time="5000" reject-policy="RETRY_ABORT"/>
                </managed-scheduled-executor-services>
            </concurrent>
            <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" jms-connection-factory="java:jboss/DefaultJMSConnectionFactory" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
</subsystem>

Note: If we were not to specify a any lookup or name attribute in the @Resource annotation, Wildfly would inject the default execution service.


Below is the full list of attributes you can use to configure your managed-scheduled-executor-service in Wildfly.
  • name - This is the name of the resource. This attribute is manditory.
  • jndi-name - The JNDI name for this resource. This attribute is manditory
  • context-service - The context service to use, if not supplied, the default context service is used.
  • thread-factory - The name of the thread factory, if not supplied, the default thread factory is used.
  • hung-task-threshold - how long, in milliseconds, to allow threads to run for before they are considered unresponsive. (default=0, 0=no limit, min=0)
  • long-running-tasks - Whether this is a short or long running thread. (default=false)
  • core-threads - The number of threads within the executors thread pool, including idle threads.  This attribute is mandatory. (min=0)
  • keepalive-time - How long threads can remain idle when the number of threads is greater than the core-thread size. This attribute is manditory. (min=0, default=60000)
  • reject-policy - How to handle a failed task. 'ABORT' will cause an exception to be thrown; 'RETRY_ABORT' which will cause a retry, and then an abort if the retry fails. (default=ABORT) 
To view your executor in the Wildfly Admin console, login, and navigate to Runtime > JNDI View > java:jboss > ee > concurrent.  Here, you can see all the concurrent resources including your newly configured scheduledexecutor.


Note: You will notice that I have used the JNDI name of java:jboss/ee/concurrency/scheduler/MyScheduledExectutorService, which at first glance looks excessively long. Using this path ensures that the resource shows under concurrency/scheduler list in the Wildfly admin console.



September 18, 2013

Simple Concurrency Example with Wildfly

Concurrency is the ability to run several threads in parallel, within a single process.

Since its origin Java has had java.lang.Thread for dealing with concurrency. With Java SE 5 improved support was added with the java.util.concurrency package.

Enterprise containers manage their own thread pools and ensure that the other Java EE services are available to these threads. Creating threads using the concurrency API, provided by Java SE, is not recommended in an enterprise environment as there is no guarantee that your thread would have access to these services.

By using the new Concurrency Utils you can run asynchronous operations with java.util.concurrent.ExecutorService which will result in your thread being managed by the container.

The main concurrency components are:

ManagedExecutorService - used to execute tasks in a second thread. These threads are started and managed by the container

ManagedScheduledExecutorService - Same as the ManagedExecutorService except that you can schedule the thread to start as specific times

ContextService - used for creating dynamic proxy objects

ManagedThreadFactory - used by applications to create threads that are managed by the container

Example

This example demonstrates the use of the ManagedExecutorService.

First we need to create a task object that implements Callable. Within the call() method we will define the work that we want carried out in a separate thread.

public class ReportTask implements Callable<Report> {

    Logger logger = Logger.getLogger(getClass().getSimpleName());

    public Report call() {
        try {
            Thread.sleep(3000);
        catch (InterruptedException e) {
            logger.log(Level.SEVERE, "Thread interrupted", e);
        }
    return new Report();
    }
}

Then we need to invoke the task by passing it though to the submit() method of the ManagedExecutorService.

@Stateless
public class ReportBean {

    @Resource
    private ManagedExecutorService executorService;

    public void runReports() {
        ReportTask reportTask = new ReportTask();
        Future<Report> future = executorService.submit(reportTask);
    }
}

By not specifying the lookup attribute of the @Resource annotation, Wildfly will inject a managed executor service with default configuration as configured within the ee subsystem in standalone.xml.

<subsystem xmlns="urn:jboss:domain:ee:2.0">
    <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
    <jboss-descriptor-property-replacement>true</jboss-descriptor-property-replacement>
    <concurrent>
        <default-context-service/>
        <default-managed-thread-factory/>
        <default-managed-executor-service hung-task-threshold="60000" core-threads="5" max-threads="25" keepalive-time="5000"/>
        <default-managed-scheduled-executor-service hung-task-threshold="60000" core-threads="2" keepalive-time="3000"/>
    </concurrent>
</subsystem>