The Spring Framework aka Spring, is an open source java application framework. Signature services of the Spring Framework include Inversion of Control (IoC) and Aspect-oriented programming (AOP). For more information about Spring and SpringSource, see SpringSource.org.

WaveMaker is built upon Spring. Project services are all created as XML declared spring beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. ref

Webinar

View Recording

Slides from Webinar (PDF)

Dependency Injection

In dependency injection (DI), beans declare their dependencies and the container server passes or injects those objects (parameters) to your java objects (project services).

Using dependency injection with your java code in WaveMaker is simple. You only need to declare the member in your class and the property reference in your bean declaration.

DI Example

This example shows getting the JDBC URL of an imported data model in a java service using setter-based injection. This would be used if we wanted to perform database operations directly from our java service. See also using a database service from java to use existing connections.

Example project DependencyInjectionJDBC.zip (WaveMaker 6.3)

Import a Database

In this example we import the HSQLDB database "Rolodex"

Add java service

We called our service: MyDataService With a package and class of: dev.wavemaker.example.MyDataService Leave the class to the default generated for now and save to complete generation.

Determine property to be injected

Open \services\rolodex\src\rolodex.spring.xml Find the SessionFactory property name. This is usually on line 78 for imported database services. For this example, we want the dataSource property. com.wavemaker.runtime.data.spring.ConfigurationAndSessionFactoryBean is the wavemaker class that extends org.springframework.orm.hibernate3.LocalSessionFactoryBean We want the same dataSource provided to the SessionFactory rolodex.spring.xml:

<!-- A unique name for this SessionFactory's configuration -->
        <property name="name" value="rolodex"/>
        <property name="dataSource" ref="rolodexDataSource"/>

Modify java service definition

Open servicesMyDataServiceMyDataService.spring.xml and make the following modifications:

  • Import rolodex.spring.xml
  • Add the dataSource property from the rolodexDataSource
Here's what the resultant file looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <!-- Import Added -->
    <import resource="classpath:rolodex.spring.xml"/>
 <!-- trailing "/" at column 108 removed so we add property-->
    <bean lazy-init="true" scope="singleton" class="dev.wavemaker.example.MyDataService" id="MyDataService">
    
 <!-- dataSource property, ref bean and closing /bean added-->
        <property name="dataSource">
            <ref bean="rolodexDataSource"/>
        </property>
 <!-- replaces / removed from line 7 -->       
    </bean>
    
 <!-- No modifications beyond this point-->   
    <bean lazy-init="false" scope="singleton" class="com.wavemaker.runtime.service.reflect.ReflectServiceWire">
        <property value="MyDataService" name="serviceId"/>
        <property ref="JavaService" name="serviceType"/>
    </bean>
</beans>

Implement Java Service

Now we can fully implement our java service. In this example we use a required setter for injection.

package dev.wavemaker.example;
import com.wavemaker.common.WMRuntimeException;
import com.wavemaker.runtime.service.annotations.ExposeToClient;
import com.wavemaker.runtime.service.annotations.HideFromClient;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.sql. * ;
/**
 *  This is an example of using dependency injection to get the JDBC connection URL from an imported database service
 *  This class might use the dataSource to open it's own connection to the database.
 *
 */
public class MyDataService extends com.wavemaker.runtime.javaservice.JavaServiceSuperClass {
     
    private Connection conn;
    private DriverManagerDataSource dataSource;
    
    // Setter for dataSource injection by container
    // Using @Required to ensure depenendcy is set
    // Using @HideFromClient to prevent IllagalArgumentException in 6.3, fixed for 6.4
    @HideFromClient
    @Required
    public void setDataSource(DriverManagerDataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    
    // The @HideFromClient annoation prevents the error
    // IllegalArgumentException: com.wavemaker.json$NoGetterInType: args don't match.  msg requires: 3 passed in: 1
    // Not needed with WM 6.4
    @HideFromClient
    public Connection getConnection() {
        try {
            if (conn == null) conn = dataSource.getConnection();
        }
        catch (SQLException e) {
            e.printStackTrace();
            throw new WMRuntimeException(e);
        }
        return conn;
    }
    
    // Method for returning URL to client for demo
    // public methods are exposed to client by default
    public String getURL() {
        String result = null;
        try {
            conn = this.getConnection();
            result = conn.getMetaData().getURL();
        } catch (SQLException e) {
            e.printStackTrace();
            throw new WMRuntimeException(e);
        }
        return result;
    }
    //For logging, extension of JavaServiceSuperClass is optional
    public MyDataService() {
        super(INFO);
    }
}

Spring Integration 2.0

Example of using Spring Integration
Example project export SpringIntegrationFileServerExample.zip (version 6.3)

This example takes Josh Long's file system example and integrates it into WaveMaker. The spring example source is available from src.springframework.org

NOTE: This example uses the project java service as the service activator. The service activator would normally not be exposed the client. This example also demonstrates that we can use component scanning and the @Component stereotype annotation in project service classes.

The following sections walk through how the example was built

Add jar files to lib

Download Spring Integration and copy:

  • spring-integration-core-2.0.4.RELEASE.jar
  • spring-integration-file-2.0.4.RELEASE.jar
to the project lib folder

Add Java Service to project

Add a Java Service to the project. In this example the Service name is ServerFileProcessor and the class and package name is dev.wavemaker.fileProcessing.InboundFileProcessor

Here is our InboundFileProcessor class

package dev.wavemaker.fileProcessing;
import org.springframework.integration.annotation.*;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Map;
/**
 * Based on the example from:
 * http://blog.springsource.com/2010/08/23/secure-file-transfer-the-only-way-to-fly/
 * 
 * Copy spring-integration-core-2.x to project lib folder for this to compile
 * 
 */
@Component  //Already a bean since using project service
public class InboundFileProcessor  {
 
      @ServiceActivator
      public void onNewFileArrival(
          @Headers Map<String, Object> headers,
          @Payload File file) {
              
              
        System.out.printf("***\t A new file has arrived deposited into " +
                          "the inbox folder at the absolute " +
                          "path %s \n", file.getAbsolutePath());
        System.out.println("The headers are:");
        for (String k : headers.keySet())
            System.out.println(String.format("%s=%s", k, headers.get(k)));
    }
    
}

Define Service Activator

Save this as ServerFileProcessor.xml in WEB-INF

This file defines the inbound file adapter, mapping the arrival of files to your service-activator

<beans:beans
        xmlns="http://www.springframework.org/schema/integration"
        xmlns:beans="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:file="http://www.springframework.org/schema/integration/file"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
					 	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
						http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file-2.0.xsd">
    <context:component-scan base-package="dev.wavemaker.fileProcessing"/>
    <channel id="inboundFiles"/>
    <file:inbound-channel-adapter channel="inboundFiles"
                                  auto-create-directory="true"
                                  filename-pattern="test*"
                                  directory="#{systemProperties['user.home']}/Inbox">
        <poller fixed-rate="10000"/>
    </file:inbound-channel-adapter>
    <service-activator input-channel="inboundFiles" ref="ServerFileProcessor" method="onNewFileArrival"/>
 
</beans:beans>

Import ServerFileProcessor.xml

In project-spring.xml import ServerFileProcessor.xml

<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:aop="http://www.springframework.org/schema/aop"
		xsi:schemaLocation="http://www.springframework.org/schema/beans
			http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
			http://www.springframework.org/schema/aop
			http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	 <!-- Import our Integration config -->				
   <import resource="ServerFileProcessor.xml"/>
</beans>

Run and Test

Run the project. If the project fails to deploy, you have an error, probably in the xml. To see details about the error, view the wm.log file. XML errors are logged before the testrunstart and failed to start error.

Our ServerFileProcessor.xml specifies that Spring Integration will auto create the inbox directory in your user home folder. The poller is set to every 10 seconds looking for files starting with test. Save a file to the inbox folder in user home with a file name that starts with "test". In the wm.log you should see a message from the onNewFileArrival function:

***      A new file has arrived deposited into the inbox folder at the absolute path C:\Users\ecallahan\Inbox\testme.txt
The headers are:
timestamp=1311722647762
id=841fe34e-cdf9-4ac0-bb19-84c56d29378c

Congratulations. You have integrated Spring Integration into your WaveMaker project.

Troubleshooting

The most common problems are with XML errors. These will cause the application to fail to start

FAIL - Deployed application at context path /TestSI but context failed to start

To understand the failure, look for errors prior to testrunstart. This is where you will find parser errors such as:

Caused by: org.xml.sax.SAXParseException: Element type "import" must be followed by either attribute specifications, ">" or "/>".

The log4j.properties file includes three spring framework loggers. Enabling these to debug is very verbose.

## Spring
log4j.category.org.springframework=debug
# Spring ClassUtils
#log4j.logger.org.springframework.util.ClassUtils=debug
log4j.category.org.springframework.web.servlet.view=debug


      Share/Bookmark
© 2004-  WaveMaker, Inc