tag:blogger.com,1999:blog-91396618769436221352024-03-12T19:56:28.778-07:00Bits and KitesAnonymoushttp://www.blogger.com/profile/17157288059906838258noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-9139661876943622135.post-2398772086514929442015-03-02T14:20:00.000-08:002015-03-02T14:25:59.595-08:00Spring and Node.js<p>Spring is a very powerful Java framework to build strong backends. And with Spring Boot it puts back the fun. But there is a big weakness regarding frontend technologies, as it is not designed for that. In this domain young techniques like Node.js and NPM support the newest and hottest stuff.<br />
This post shows a way how to combine Spring Boot and Node.js in one project and get use of powerful tools like Grunt, Bower, Karma, and PhantomJS. The full sourcecode is available on <a href="https://github.com/cnagel/spring-node.js" target="_blank">Github</a>.</p>
<h2>Maven is King</h2>
<p>To combine the Java and the Javascript world we will use Maven. If you prefer Gradle more, this is no problem. The used Maven plugins should work there too.</p>
<h3>Maven Node.js Plugin</h3>
<p>There exist some Maven plugins to have control on Node.js. In this post I chosed <a href="https://github.com/eirslett/frontend-maven-plugin" target="_blank">frontend-maven-plugin</a> as it does the job very good and is easy to configure.<br />
In the first step the plugin downloads the operating system specific Node.js distribution and stores it in the local project folder. You can adjust the Node.js and NPM version by parameters. If you are curios why I'm using some older versions - it's because I got problems with the <code>node-sass</code> plugin with the latest one.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<execution>
<id>install-node-npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v0.10.32</nodeVersion>
<npmVersion>2.1.8</npmVersion>
</configuration>
</execution></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<p>After installing Node.js and NPM we advice the plugin to install all NPM packages defined in <code>package.json</code> in the next step. NPM is a dependency manager - like a little Maven for Node.js.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<execution>
<id>npm-install-modules</id>
<goals>
<goal>npm</goal>
</goals>
</execution></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<p>One of the packages defined in this project is Grunt - a task runner for Node.js. There we will define all steps to prepare our frontend resources. As Grunt works with cascading tasks we will configure it to start the <code>dist</code> task which will include all other neccessary tasks. If you prefer Gulp, there is a possibility to start it by the Maven plugin too.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<execution>
<id>grunt-dist</id>
<goals>
<goal>grunt</goal>
</goals>
<configuration>
<arguments>dist</arguments>
</configuration>
</execution></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<p>But Node.js is not only good to prepare frontend resources like CSS and JS files. It offers methods to execute tests for your Javascript files. The tests get executed by <a href="http://karma-runner.github.io" target="_blank">Karma</a> and guess what - there is a nice Maven goal defined in the plugin too. We just have to define a configuration file which will be described later.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<execution>
<id>karma-test</id>
<phase>test</phase>
<goals>
<goal>karma</goal>
</goals>
<configuration>
<karmaConfPath>src/test/webapp/WEB-INF/javascript/karma.conf.ci.js</karmaConfPath>
</configuration>
</execution></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<h3>Spring Boot</h3>
<p>As described <a href="http://bits-and-kites.blogspot.de/2014/01/spring-boot-example.html">before</a> this is a very easy step. It just needs to add the <code>spring-boot-starter-parent</code> POM and a <code>spring-boot-web-starter</code>. As we want to use the Thymeleaf template engine, we will use the <code>spring-boot-starter-thymeleaf</code> starter.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<p>As we want to be able to start the project by executing <kbd>mvn spring-boot:run</kbd>, we have to add the frontend resources from Node.js by extending the <code>spring-boot-maven-plugin</code> configuration. There we add the <code>${project.build.directory}/grunt</code> folder as resource. Later we will instrument Grunt to put the compiled resources there.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
<configuration>
<folders>
<folder>${project.build.directory}/grunt</folder>
</folders>
</configuration>
</plugin></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<h3>Maven WAR plugin</h3>
<p>The Spring Boot command is only one possibility to launch our application. Another way is to create an Uber-WAR by <kbd>mvn package</kbd>. Also knowing nothing about our generated frontend resources, we have to instrument the <code>maven-war-plugin</code> to copy them into the new container too.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<packagingExcludes>WEB-INF/lib/tomcat-*.jar</packagingExcludes>
<webResources>
<resource>
<directory>${project.build.directory}/grunt/public</directory>
<targetPath>public</targetPath>
</resource>
</webResources>
</configuration>
</plugin></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<h2>Short Java Code</h2>
<p>As this post is more about Node.js and not Spring, we will just create the Spring application class and define a controller to display a template.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package cnagel.spring_node;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@SpringBootApplication
@Controller
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@RequestMapping("/")
public String index() {
return "index";
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/src/main/java/cnagel/springnode/Application.java" target="_blank">src/main/java/cnagel/springnode/Application.java</a></div>
</div>
<p>Our template looks very basic and just includes the <code>styles.css</code> and <code>scripts.js</code> resource. The ThymeLeaf template engine helps us to get the correct path for them.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-html linenums pre-scrollable">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Spring-Node.js</title>
<link th:href="@{/styles.css}" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container">
<h1>Spring-Node.js</h1>
</div>
<script type="text/javascript" th:src="@{/scripts.js}"></script>
</body>
</html></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/src/main/resources/templates/index.html" target="_blank">src/main/resources/templates/index.html</a></div>
</div>
<h2>Node.js Modules and Configuration</h2>
<p>The Java part is finished - let's have a look on Node.js and supported tools. We will start with NPM and Bower, both managing dependencies. Further Grunt to coordinate all tasks, processing the resources and test the JavaScript code.</p>
<h3>NPM</h3>
<p>NPM is used to manage our Node.js package dependencies. This is done by the <code>package.json</code> file, which simply defines our needed libraries in the JSON format and adds some additional information about our project.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-jsonlinenums pre-scrollable">
{
"name": "spring-node.js",
"description": "A Spring Node.js project",
"version": "0.0.1",
"repository": "https://github.com/cnagel/spring-node.js.git",
"devDependencies": {
"bower": "~1.3.12",
"grunt": "~0.4.5",
"grunt-bower-install-simple": "1.1.0",
"grunt-cli": "~0.1.13",
"grunt-concurrent": "~1.0.0",
"grunt-contrib-uglify": "~0.7.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-karma": "~0.10.1",
"grunt-sass": "~0.18.0",
"karma": "~0.12.31",
"karma-jasmine": "~0.3.5",
"karma-phantomjs-launcher": "~0.1.4"
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/package.json" target="_blank">package.json</a></div>
</div>
<p>Nothing special in there, if you want more information about the used packages have a look on the specific NPM or GitHub sites.</p>
<h3>Bower</h3>
<p><a href="http://bower.io" target="_blank">Bower</a> is a dependency manager for frontend resources. We will use it to embed <a href="http://getbootstrap.com" target="_blank">Bootstrap</a> as CSS framework, furhter <a href="http://modernizr.com" target="_blank">Modernizr</a> and <a href="http://jquery.com" target="_blank">jQuery</a> as Javascript examples.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-json linenums pre-scrollable">
{
"name": "spring-node.js",
"version": "0.0.1",
"dependencies": {
"bootstrap-sass": "twbs/bootstrap-sass#~3.3.3",
"modernizr": "2.8.3",
"jquery": "2.1.3"
},
"devDependencies": {
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/bower.json" target="_blank">bower.json</a></div>
</div>
<p>But no fear, this is a frontend resource agnostic tutorial, you can choose any other CSS or Javascript framework supported by Bower. I just want to give an overview how to use the manager. We will later see how to get them into our compiled code.</p>
<h2>Stylesheets and Javascript Resources</h2>
<p>While Bower defines our frontend resource libraries, we now want to write our custom CSS and Javascript code. As CSS alone has some disadvantages we will use <a href="http://sass-lang.com" target="_blank">SASS</a>. And as a good coder we will add some tests to our JavaScript code.</p>
<h3>Sass</h3>
<p>There exist several ways to embedd multiple SASS files into one, and I will choose the <code>@import</code> way. There we have to define all included SASS files manually. In this case it will be the Bootstrap library which comes with all the CSS sugar we love. Further we add a new CSS directive setting the color of our headline to the brand primary color defined by a Bootstrap variable.<br />
Tip: Before including Bootstrap, you can pre define any Bootstrap SASS variable!</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-sass linenums pre-scrollable">
@import 'bootstrap';
body h1 { color: $brand-primary; }</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/src/main/webapp/WEB-INF/sass/app.scss" target="_blank">src/main/webapp/WEB-INF/sass/app.scss</a></div>
</div>
<h3>Javascript</h3>
<p>In this example we will just have one very basic Javascript files. It defines a <code>sayHello</code> method, excecutes it if the DOM is loaded and prints the output to the console.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-javascript linenums pre-scrollable">
function sayHello(name) {
return "Hello " + name + "!";
}
$(function() {
console.log(sayHello("to everyone"));
});</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/src/main/webapp/WEB-INF/javascript/app.js" target="_blank">src/main/webapp/WEB-INF/javascript/app.js</a></div>
</div>
<h3>Test configuration</h3>
<p>To execute Javascript tests, we will use the three following techniques:
<ul>
<li><a href="http://karma-runner.github.io" target="_blank">Karma</a> - Test launcher</li>
<li><a href="http://jasmine.github.io" target="_blank">Jasmine</a> - Spec framework</li>
<li><a href="http://phantomjs.org" target=""_blank">PhantomJS</a> - Headless browser</li>
</ul>
As Karma gets executed by the Grunt task runner, we just need to define a Karma configuration file. Following code shows how to set PhantomJS and Jasmine for the tests. Furhter we have to define the locations of jQuery, our Javascript code and the tests.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-javascript linenums pre-scrollable">
module.exports = function(config) {
config.set({
basePath: '../../../../..',
frameworks: ['jasmine'],
files: [
'.bowercomponents/jquery/dist/jquery.js',
'src/main/webapp/WEB-INF/javascript/*.js',
'src/test/webapp/WEB-INF/javascript/*.js'
],
exclude: ['src/test/webapp/WEB-INF/javascript/karma.conf*.js'],
reporters: ['progress'],
port: 9876,
logLevel: config.LOG_INFO,
browsers: ['PhantomJS'],
singleRun: false,
autoWatch: true,
plugins: [
'karma-jasmine',
'karma-phantomjs-launcher'
]
});
};</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/src/test/webapp/WEB-INF/javascript/karma.conf.js" target="_blank">src/test/webapp/WEB-INF/javascript/karma.conf.js</a></div>
</div>
<p>The Karma configuration is used for development and will always execute the tests if there are changes on the source files. To execute the test only once and terminate the testing engine we will use a second config file, which embeds this one and overrides the <code>autoWatch</code> settings. his one will be used by Maven.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-javascript linenums pre-scrollable">
var baseConfig = require('./karma.conf.js');
module.exports = function(config){
baseConfig(config);
config.set({
singleRun: true,
autoWatch: false
});
};</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/src/test/webapp/WEB-INF/javascript/karma.conf.ci.js" target="_blank">src/test/webapp/WEB-INF/javascript/karma.conf.ci.js</a></div>
</div>
<h3>Javascript Tests</h3>
<p>In parallel to our Javascript source code, the tests wil be short and easy too. Doing this in a more behaviour driven fashion we write them in Jasmine syntax.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-javascript linenums pre-scrollable">
describe('The sayHello function', function(){
it('should greet with name', function(){
expect(sayHello("World")).toBe("Hello World!");
});
});</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/src/test/webapp/WEB-INF/javascript/appSpec.js" target="_blank">src/test/webapp/WEB-INF/javascript/appSpec.js</a></div>
</div>
<h3>Grunt</h3>
<p>After defining our resources, code, and tests, it needs a task runner to get it all together. As configured in the pom.xml we will use Grunt and it just needs an additional file to define our tasks. The file starts by defining help variables for neccessary code and resource directories. Further we have to import used plugins provided by the NPM package manager. Then comes the tricky part, where the plugins get configured and we have to provide the correct paths and parameters.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-javascript linenums pre-scrollable">
<!-- Bower -->
"bower-install-simple" : {
options : {
directory : config.bower,
},
dist : {
options : {
production : true,
},
},
},
<!-- Karma -->
karma : {
unit : {
configFile : '<%= config.js_unit %>/karma.conf.js'
},
integration : {
configFile : '<%= config.js_integration %>/karma.conf.js'
},
},
<!-- SASS -->
sass : {
dist : {
options : {
outputStyle : 'compressed',
includePaths : [ '<%= config.bower %>/bootstrap-sass/assets/stylesheets/' ],
sourceMap : true
},
files : {
'<%= config.dest %>/styles.css' : '<%= config.sass %>/app.scss'
}
}
},
<!-- JS uglify -->
uglify : {
dist : {
options : {
sourceMap : true,
},
files : {
'<%= config.dest %>/scripts.js' : [
'<%= config.bower %>/modernizr/modernizr.js',
'<%= config.bower %>/jquery/dist/jquery.js',
'<%= config.bower %>/bootstrap-sass/assets/javascripts/bootstrap.js',
'<%= config.js %>/**/*.js', ]
}
}
}
</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-node.js/blob/master/Gruntfile.js" target="_blank">Gruntfile.js</a></div>
</div>
<h2>Get it run</h2>
<p>By execution the mvn <code>test</code> goal, all frontend resources get collected, merged into the corresponding file and the Javascript tests executed. You will see a Karma error message if the test fails and Maven will stop too.</p>
<p>While writing your CSS or JS code, there exists also the opportunity to recompile and test the code each time you make changes. Therefore you have to start Grunt wih the default task: <kbd>node_modules/grunt-cli/bin/grunt</kbd>.</p>Anonymoushttp://www.blogger.com/profile/17157288059906838258noreply@blogger.com0Berlin, Deutschland52.520006599999988 13.40495399999997552.210736099999991 12.759506999999974 52.829277099999985 14.050400999999976tag:blogger.com,1999:blog-9139661876943622135.post-76484261422505730192014-10-20T13:12:00.002-07:002014-12-20T05:43:35.897-08:00Spring Cloud Eureka Server<img src="http://projects.spring.io/spring-cloud/img/project-icon-large.png" alt="Spring Cloud" class="img-thumbnail" style="max-width: 25%; float: left; margin-right: 30px;"/>
<p>One of the youngest children in the Spring stack is <a href="http://projects.spring.io/spring-cloud/" target="_blank">Spring Cloud</a> which bundles tools to organize and manage distributed systems. There exist components being self developed by the Spring team like <a href="http://cloud.spring.io/spring-cloud-config" target="_blank">Spring Cloud Config</a> which offer a centralized configuration server.<br />
On the other hand the Spring team included components from the <a href="http://netflix.github.io/" target="_blank">Netflix Open Source Software Center</a> and embeds them in the new and easy annotation fashion known from Spring Boot. One of the main Netflix components is <a href="https://github.com/Netflix/eureka" target="_blnak">Eureka</a>, a REST based service registration and discovery service.</p>
<p>This post is a very simple tutorial showing how to produce an Eureka server with Spring Cloud. The source code is available on <a href="https://github.com/cnagel/spring-eureka-server-example" target="_blank">GitHub</a>.</p>
<h4>Maven configuration</h4>
<p>The Maven configuration for this project is very straight-forward:
<ul>
<li>import the Spring Cloud starter pom for Maven dependency settings</li>
<li>add a dependency to the Spring Cloud starter Eureka server</li>
<li>extend the Spring Boot Maven plugin configuration</li>
<li>add milestone repository</li>
</ul>
Last step is needed while Spring Cloud isn't released in 1.0 yet.
</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cnagel</groupId>
<artifactId>spring-eureka-server-example</artifactId>
<version>0.0.2-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud.version>1.0.0.RC1</spring-cloud.version>
<spring-boot.version>1.2.0.RELEASE</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<requiresUnpack>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-core</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-client</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-eureka-server-example/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<h4>Java Application class</h4>
<p>While everything is pre-configured by Spring Cloud it just needs to add some annotations to the application class. <code>@ComponentScan</code>, <code>@Configuration</code> and <code>@EnableAutoConfiguration</code> are typically Spring annotations initiating a Spring app the Boot way. <code>@EnableEurekaServer</code> loads the Eureka server.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package cnagel.spring_eureka_server_example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan
@Configuration
@EnableAutoConfiguration
@EnableEurekaServer
public class EurekaServerApp {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApp.class, args);
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-eureka-server-example/blob/master/src/main/java/cnagel/spring_eureka_server_example/EurekaServerApp.java" target="_blank">src/main/java/cnagel/spring_eureka_server_example/EurekaServerApp.java</a></div>
</div>
<h4>Application properties</h4>
<p>Properties are defined in the <code>application.yml</code> which is loaded automatically by Spring Boot.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-yaml linenums pre-scrollable">
spring:
application:
name: eureka-server
server:
port: 8761
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
waitTimeInMsWhenSyncEmpty: 0
logging:
level:
com.netflix: 'WARN'
org.springframework.cloud: 'WARN'</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-eureka-server-example/blob/master/src/main/resources/application.yml" target="_blank">src/main/resources/application.yml</a></div>
</div>
<h4>Starting the Eureka server</h4>
<p>Last but not least the server gets started by executing <kbd>mvn spring-boot:run</kbd>. There exists a dashboard on <code>http://localhost:8761</code> giving useful information about the Eureka server. The REST representation of registered services is available at <code>http://localhost:8761/eureka/apps</code>.</p>Anonymoushttp://www.blogger.com/profile/17157288059906838258noreply@blogger.com0tag:blogger.com,1999:blog-9139661876943622135.post-68085115964449141172014-03-20T07:37:00.002-07:002014-03-24T06:57:01.266-07:00Scalate integration in Spring Boot<img src="http://scalate.fusesource.org/images/project-logo.png" alt="MongoDB" class="img-thumbnail" style="max-width: 50%; float: left; margin-right: 30px;"/>
<p>Developing the <a href="https://www.surpreso.com/" target="_blank">Surpreso</a> frontend in Rails I really liked to write my views with the <a href="http://haml.info/" target="_blank">HAML</a> template engine. Investing more time with Spring Boot I searched for a Java pendant and got aware of <a href="http://scalate.fusesource.org/" target="_blank">Scalate</a>, the scala template engine.</p>
At this time Scalate supports 4 template formats:
<ul>
<li>Mustache</li>
<li>SSP</li>
<li>Scaml (which is equal to HAML)</li>
<li>Jade (which is little bit nicer than HAML :-)</li>
</ul>
<p>Integrating it in Spring Boot seemed very easy and I wrote a small Git <a href="https://github.com/spring-projects/spring-boot/pull/372" target="_blank">pull-request</a> with passing tests. Afterwards I wanted to write this small tutorial to do the steps manually and found some ugly packaging hooks. The post shows how to pass them and I hope to correct the pull-request in the next days.<br />
As always you can find the source code for this project on <a href="https://github.com/cnagel/spring-scalate" target="_blank">GitHub</a>.</p>
<h4>Maven configuration</h4>
<p>In opposite to the Spring Boot defaults I store my template files under <code>src/main/webapp/WEB-INFO/templates</code>. If you work with Eclipse it won't reload the complete server project when you change your views. But this needs some management in the <code>pom.xml</code> to get the tests run.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<groupid>cnagel</groupid>
<artifactid>spring-scalate</artifactid>
<version>0.0.1-SNAPSHOT</version>
<!-- The packaging format, use jar for standalone projects -->
<packaging>war</packaging>
<properties>
<!-- The main class to start by executing java -jar -->
<start-class>cnagel.spring_scalate.Application</start-class>
<project .build.sourceencoding="">UTF-8</project>
<project .reporting.outputencoding="">UTF-8</project>
<scala .version="">2.10.0</scala>
<scalate .version="">1.6.1</scalate>
</properties>
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-parent</artifactid>
<version>1.0.0.RC4</version>
</parent>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>
<!-- Spring Tomcat libs needed for Eclipse -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-tomcat</artifactid>
<scope>provided</scope>
</dependency>
<!-- Scalate -->
<dependency>
<groupid>org.fusesource.scalate</groupid>
<artifactid>scalate-spring-mvc_2.10</artifactid>
<version>${scalate.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Use plugin to package as an executable JAR -->
<plugin>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-maven-plugin</artifactid>
</plugin>
<!-- Scalate template pre compiler -->
<plugin>
<groupid>org.fusesource.scalate</groupid>
<artifactid>maven-scalate-plugin_2.10</artifactid>
<version>${scalate.version}</version>
<executions>
<execution>
<goals>
<goal>precompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Create classes and lib folder in test phase for Scalate -->
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-antrun-plugin</artifactid>
<executions>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<mkdir dir="target/test-classes/WEB-INF/classes">
<mkdir dir="target/test-classes/WEB-INF/lib">
</mkdir></mkdir></tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<pluginmanagement>
<plugins>
<!-- Executes the antrun plugin in Eclipse too -->
<plugin>
<groupid>org.eclipse.m2e</groupid>
<artifactid>lifecycle-mapping</artifactid>
<version>1.0.0</version>
<configuration>
<lifecyclemappingmetadata>
<pluginexecutions>
<pluginexecution>
<pluginexecutionfilter>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-antrun-plugin</artifactid>
<versionrange>1.7</versionrange>
<goals>
<goal>run</goal>
</goals>
</pluginexecutionfilter>
<action>
<execute>
</execute></action>
</pluginexecution>
</pluginexecutions>
</lifecyclemappingmetadata>
</configuration>
</plugin>
</plugins>
</pluginmanagement>
<!-- Adds the WEB-INF folder to the test-classes directory to include templates -->
<testresources>
<testresource>
<directory>src/main/webapp</directory>
</testresource>
</testresources>
</build>
<!-- Allow access to Spring milestones -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginrepositories>
<pluginrepository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</pluginrepository>
</pluginrepositories>
</project></pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-scalate/blob/master/pom.xml" target="_blank">pom.xml</a></div>
</div>
<p>OK, this one looks big, lets go through the interesting parts:<br />
<ul>
<li>Packaging format is war to deploy the project in the STS or Eclipse server</li>
<li>Therefore we have to add a dependency to the <code>spring-boot-starter-tomcat</code> artifact which includes neccessary tomcat libraries</li>
<li>We have to add the <code>scalate</code> dependency, fortunately there exists a Spring MVC artifact</li>
<li>Scalate has a nice feature to precompile your templates in the maven package cycle and we only have to include the <code>maven-scalate-plugin_2.10</code></li>
<li>Executing the tests Scalate logs some ugly warnings with exception stacktraces for missing folders (<code>lib</code> and <code>classes</code>) - we will use the <code>maven-antrun-plugin</code> to create the temporary directories in the test phase</li>
<li>Eclipse ignores this plugin and therefore we have to force the execution in the IDE too</li>
<li>Last we have to put the <code>src/main/webapp</code> folder to our test resources. Otherwise it is not possible to check the output of the templates in the tests</li>
</ul>
</p>
<h4>Spring configuration</h4>
<p>For simplicity I put the Spring Boot configuration into the <code>Application</code> class.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package cnagel.spring_scalate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan("cnagel.spring_scalate")
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-scalate/blob/master/src/main/java/cnagel/spring_scalate/Application.java" target="_blank">src/main/java/cnagel/spring_scalate/Application.java</a></div>
</div>
<p>Additionally it needs a Servlet initializer to run the application in a server like Tomcat or Jetty.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package cnagel.spring_scalate;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class WebXml extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-scalate/blob/master/src/main/java/cnagel/spring_scalate/WebXml.java" target="_blank">src/main/java/cnagel/spring_scalate/WebXml.java</a></div>
</div>
<p>Being the default bootstrapping process it now needs a Scalate configuration to load the template engine.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package cnagel.spring_scalate.config;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.ServletContext;
import org.fusesource.scalate.layout.DefaultLayoutStrategy;
import org.fusesource.scalate.servlet.Config;
import org.fusesource.scalate.servlet.ServletTemplateEngine;
import org.fusesource.scalate.spring.view.ScalateViewResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.context.ServletContextAware;
import scala.collection.JavaConversions;
@Configuration
public class ScalateConfig {
public static final String DEFAULT_PREFIX = "/WEB-INF/templates/";
public static final String DEFAULT_SUFFIX = ".jade";
public static final String DEFAULT_LAYOUT = "/WEB-INF/templates/layouts/default.jade";
@Configuration
protected static class ScalateConfigConfiguration implements ServletContextAware {
private ServletContext servletContext;
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Bean
public Config config() {
return new Config() {
@Override
public ServletContext getServletContext() {
return servletContext;
}
@Override
public String getName() {
return "unknown";
}
@Override
public Enumeration getInitParameterNames() {
return null;
}
@Override
public String getInitParameter(String name) {
return null;
}
};
}
}
@Configuration
protected static class ScalateServletTemplateEngineConfiguration {
@Autowired
private Config config;
@Bean
public ServletTemplateEngine servletTemplateEngine() {
ServletTemplateEngine engine = new ServletTemplateEngine(config);
List<string> layouts = new ArrayList<string>(1);
layouts.add(DEFAULT_LAYOUT);
engine.layoutStrategy_$eq(new DefaultLayoutStrategy(engine,
JavaConversions.asScalaBuffer(layouts)));
return engine;
}
}
@Configuration
protected static class ScalateViewResolverConfiguration {
@Autowired
private ServletTemplateEngine servletTemplateEngine;
@Bean
public ScalateViewResolver scalateViewResolver() {
ScalateViewResolver resolver = new ScalateViewResolver();
resolver.templateEngine_$eq(servletTemplateEngine);
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 20);
resolver.setPrefix(DEFAULT_PREFIX);
resolver.setSuffix(DEFAULT_SUFFIX);
return resolver;
}
}
}
</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-scalate/blob/master/src/main/java/cnagel/spring_scalate/config/ScalateConfig.java" target="_blank">src/main/java/cnagel/spring_scalate/config/ScalateConfig.java</a></div>
</div>
<p>In a short version, the constants in the beginning of the class set the template folder, template extension, and the default layout. Furthermore it registers the <code>ScalateViewResolver</code> to render the views.</p>
<h4>Controller</h4>
<p>To show the functionality of Scalate we will only implement a very basic controller. This one renders an index site and offers an ajax endpoint to add form inputs to a list.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package cnagel.spring_scalate.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/")
public class HomeController {
@RequestMapping(method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView mav = new ModelAndView("layout:home/index");
return mav;
}
@RequestMapping(method = RequestMethod.POST)
public ModelAndView post(@RequestParam("text") String text) {
ModelAndView mav = new ModelAndView("home/post");
mav.addObject("text", text);
return mav;
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-scalate/blob/master/src/main/java/cnagel/spring_scalate/controller/HomeController.java" target="_blank">src/main/java/cnagel/spring_scalate/controller/HomeController.java</a></div>
</div>
<p>In the <code>index</code> action we only define to render the <code>home/index</code> template. Given the default folder and extension in the configuration before, Scalate will use the <code>src/main/webapp/WEB-INF/templates/home/index.jade</code> file to render the output. The <code>layout:</code> prefix instructs Scalate to render the layout too and embed the view.<br />
The <code>post</code> action handles incoming POST requests. Being an ajax endpoint it just renders the <code>home/post</code> view without the layout and assigns the posted <code>text</code> param.</p>
<h4>Templates</h4>
<p>There exist one layout and two templates in the project. The <a href="https://github.com/cnagel/spring-scalate/blob/master/src/main/webapp/WEB-INF/templates/layouts/default.jade" target="_blank">layout</a> loads Twitter <a href="http://getbootstrap.com/" target="_blank">Bootstrap</a> for CSS and <a href="http://jquery.com/" target="_blank">jQuery</a> for Javascript to easy the ajax request. Have a look at it to see the cuteness of the Jade template format.<br />
The <a href="https://github.com/cnagel/spring-scalate/blob/master/src/main/webapp/WEB-INF/templates/home/index.jade">index</a> view simply renders a form with a text input. By submitting the form, an ajax request gets executed and the response appended to a list.<br />
The <a href="https://github.com/cnagel/spring-scalate/blob/master/src/main/webapp/WEB-INF/templates/home/post.jade" target="_blank">post</a> view only renders a list element with the <code>text</code> param as content.</p>
<h4>Testing</h4>
<p>The tests simply use the provided Spring MVC mocks and check if both action render the correct views and if needed the layout.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package cnagel.spring_scalate.controller;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.internal.matchers.Contains;
import org.mockito.internal.matchers.StartsWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import cnagel.spring_scalate.Application;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration("classpath:.")
public class HomeControllerTests {
@Autowired
private WebApplicationContext ctx;
private MockMvc mvc;
@Before
public void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(ctx).build();
}
@Test
public void test_index() throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string(new Contains("<label>Say what ever:</label>"))).andExpect(content().string(new StartsWith("<!DOCTYPE html>")));
}
@Test
public void test_post() throws Exception {
mvc.perform(post("/").param("text", "Hello")).andExpect(status().isOk()).andExpect(content().string(new Contains("<li>Hello</li>")));
}
}</pre>
</div>
<div class="panel-footer"><a href="https://github.com/cnagel/spring-scalate/blob/master/src/test/java/cnagel/spring_scalate/controller/HomeControllerTests.java" target="_blank">src/test/java/cnagel/spring_scalate/controller/HomeControllerTests.java</a></div>
</div>
<h4>Deployment</h4>
<p>For development issues you can run the project in the server provided by Eclipse or STS. With <kbd>mvn package</kbd> it is possible to create a jar or war file by simply switching the package format in the <code>pom.xml</code>.<br />
For me it was not possible to run the jar file under windows, having problems with illegal characters in the classpath.</p>Anonymoushttp://www.blogger.com/profile/17157288059906838258noreply@blogger.com0Berlin, Deutschland52.520006599999988 13.40495399999997552.210736099999991 12.759506999999974 52.829277099999985 14.050400999999976tag:blogger.com,1999:blog-9139661876943622135.post-27104614493065452392014-01-15T08:00:00.001-08:002014-03-24T06:12:54.126-07:00Spring MongoDB tutorial<img src="http://upload.wikimedia.org/wikipedia/commons/e/eb/MongoDB_Logo.png" alt="MongoDB" class="img-thumbnail" style="max-width: 50%; float: left; margin-right: 30px;"/>
<p>Time for a new Spring tutorial - this time we will implement a very simple but powerful data access to <a href="http://www.mongodb.org/" target="_blank">MongoDB</a>. Therefore we will use the <a href="http://projects.spring.io/spring-data/" target="_blank">Spring Data Project</a> which does the most work for us.</p>
<p>As always you can find the complete source code online on <a href="https://github.com/cnagel/spring-mongo">GitHub</a>.<br />
In my <a href="http://bits-and-kites.blogspot.de/2014/01/spring-boot-example.html">last post</a> I showed a way how to use Spring Boot for bootstraping a Spring application. This project uses the described mechanisms and for a better understanding I recommend you to read the post if you are not familar with it.</p>
<h4>Maven configuration</h4>
<p>Let's start with the <code>pom.xml</code> and add the needed dependencies.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.surpreso</groupId>
<artifactId>spring-mongo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- The packaging format, use war for web projects -->
<packaging>jar</packaging>
<properties>
<!-- The main class to start by executing java -jar -->
<start-class>com.surpreso.spring_mongo.HelloWorldApplication</start-class>
</properties>
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- YAML parser -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<scope>runtime</scope>
</dependency>
<!-- @Inject annotation -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Spring MongoDB integration -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
<!-- In-memory MongoDB for tests -->
<dependency>
<groupId>com.lordofthejars</groupId>
<artifactId>nosqlunit-mongodb</artifactId>
<version>0.7.8</version>
<scope>test</scope>
</dependency>
<!-- Google Guava library -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>15.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Use plugin to package as an executable JAR -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- Allow access to Spring milestones and snapshots -->
<repositories>
<repository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<url>http://repo.spring.io/snapshot</url>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<url>http://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
</project></pre>
</div>
<div class="panel-footer">pom.xml</div>
</div>
<p>The new part starts at line 49 where we add the <code>spring-data-mongo</code> artifact which includes the MongoDB Java Driver. To test our code I found a very nice in-memory implementation of MongoDB developed by FourSquare and avaiable on <a href="https://github.com/fakemongo/fongo" target="_blank">GitHub</a>.</p>
<p>Last we add the Google Guava library which delivers nice helping methods for <code>equals</code>, <code>toHash</code> and <code>toString</code>.</p>
<h4>Spring configuration</h4>
<p>After setting up the maven project we have to configure the Spring context. To initialize Spring Boot correctly we will use the <code>DefaultConfig</code> class introduced in the last post.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_mongo.config;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.surpreso.spring_mongo")
public class DefaultConfig {
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_mongo/config/DefaultConfig.java</div>
</div>
<p>Further we will add a MongoDB config which initializes the Spring <code>MongoTemplate</code>. Similar to <code>JdbcTemplate</code> this provides the access to our database.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_mongo.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import com.mongodb.Mongo;
import com.mongodb.MongoURI;
@Profile("default")
@Configuration
@EnableMongoRepositories(basePackages = "com.surpreso.spring_mongo.dao")
public class MongoDbConfig extends AbstractMongoConfiguration {
@Value("${mongo.url}")
private String url;
@Value("${mongo.db}")
private String databaseName;
@Override
protected String getDatabaseName() {
return databaseName;
}
@Override
public Mongo mongo() throws Exception {
return new Mongo(new MongoURI(url));
}
@Override
protected String getMappingBasePackage() {
return "com.surpreso.spring_mongo.beans";
}
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_mongo/config/MongoDbConfig.java</div>
</div>
<p>This configuration extends the Spring <code>AbstractMongoConfiguration</code>, doing the most work for us.<br />
In the test environment we want to use the in-memory MongoDB version and therefore we annotate the configuration with <code>@Profile("default")</code>. This instructs Spring to only load the configuration when there exists no profile.<br />
A further new annotation is <code>@EnableMongoRepositories(basePackages = "com.surpreso.spring_mongo.dao")</code> which tells the spring-data-mongo project where to look for MongoDB repositories.<br />
The <code>getMappingBasePackage</code> method simply defines our bean package.<br />
The values for the database name and url are stored in the <code>application.yml</code>.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-yaml linenums pre-scrollable">
mongo:
url: mongodb://127.0.0.1:27017
db: foo</pre>
</div>
<div class="panel-footer">src/main/resources/application.yml</div>
</div>
<p>For the test environment exists an own configuration.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_mongo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import com.foursquare.fongo.Fongo;
import com.mongodb.Mongo;
@Profile("test")
@Configuration
public class MongoDbTestConfig extends MongoDbConfig {
@Override
public Mongo mongo() throws Exception {
return new Fongo("foo test server").getMongo();
}
}</pre>
</div>
<div class="panel-footer">src/test/java/com/surpreso/spring_mongo/config/MongoDbTestConfig.java</div>
</div>
<p>This one extends the default MongoDB configuration but returns the in-memory implementation.</p>
<h4>Beans & Repositories</h4>
<p>In the defined mapping package we create a very simple bean called <code>Foo</code> having only an ID and a name property.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_mongo.beans;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import com.google.common.base.Objects;
@Document
public class Foo {
@Id
private Long id;
private String name;
public Foo() {
super();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!getClass().isInstance(obj)) {
return false;
}
Foo foo = (Foo) obj;
return Objects.equal(getId(), foo.getId())
&& Objects.equal(getName(), foo.getName());
}
@Override
public int hashCode() {
return Objects.hashCode(getId(), getName());
}
@Override
public String toString() {
return Objects.toStringHelper(this).addValue(getId())
.addValue(getName()).toString();
}
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_mongo/beans/Foo.java</div>
</div>
<p>The bean gets annotated by <code>@Document</code> so Spring will map it to JSON, the Default MongoDB Format, on the fly. The <code>@Id</code> annotation for the id property defines the primary key for the document in the collection.<br />
We will add <code>equals</code>, <code>hashCode</code> and <code>toString</code> methods for our tests and application output.<br />
The implementation of the <code>FooRepository</code> will be done by Spring and we only have to define an interface giving the framework the needed information.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_mongo.dao;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import com.surpreso.spring_mongo.beans.Foo;
@Repository
public interface FooRepository extends MongoRepository<Foo, Long> {
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_mongo/dao/FooRepository.java</div>
</div>
<p>Sweeeeet! That's all. Now we have a working data access for our Foos in MongoDB.</p>
<h4>Testing</h4>
<p>I know, we should not test the functionality of Spring if it implements the <code>FooRepository</code> in the right way, but let's do it to see if our fake MongoDB gets initialized and the data access works.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_mongo.dao;
import static org.junit.Assert.assertEquals;
import javax.inject.Inject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationContextLoader;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.surpreso.spring_mongo.beans.Foo;
import com.surpreso.spring_mongo.config.DefaultConfig;
@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { DefaultConfig.class }, loader = SpringApplicationContextLoader.class)
public class FooRepositoryTests {
@Inject
FooRepository repo;
@Test
public void test_basicOperations() throws Exception {
// check if collection is empty
assertEquals(0, repo.count());
// create new document
Foo foo = new Foo();
foo.setId(1l);
foo.setName("Foo 1");
repo.save(foo);
// store new document
repo.save(foo);
// check if document stored
assertEquals(1, repo.count());
// check stored document
assertEquals(foo, repo.findOne(1l));
}
}</pre>
</div>
<div class="panel-footer">src/test/java/com/surpreso/spring_mongo/dao/FooRepositoryTests.java</div>
</div>
<p>The test context initialization is familiar to my last post and the test itself only perfoms some Basic operations on the database. Using the in-memory MongoDB you can run this test without a working MongoDB Environment.</p>
<h4>Deployment & Execution</h4>
<p>Last we will implement an application which uses a real MongoDB Environment (<a href="http://docs.mongodb.org/manual/installation/" target="_blank">install instructions</a>).</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_mongo;
import javax.inject.Inject;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Import;
import com.surpreso.spring_mongo.beans.Foo;
import com.surpreso.spring_mongo.config.DefaultConfig;
import com.surpreso.spring_mongo.dao.FooRepository;
@Import(DefaultConfig.class)
public class HelloWorldApplication implements CommandLineRunner {
@Inject
private FooRepository repo;
public static void main(String... args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
public void run(String... args) throws Exception {
// get size of the collection
long count = repo.count();
// create new document
Foo foo = new Foo();
foo.setId(count + 1);
foo.setName("Foo " + foo.getId());
// add new document to collection
repo.save(foo);
// output collection info
System.out.println("= Found " + repo.count()
+ " documents in Foo collection");
for (Foo f : repo.findAll()) {
System.out.println("+ " + f);
}
}
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_mongo/HelloWorldApplication.java</div>
</div>
<p>The application just creates a new document in your collection and outputs all stored records. With each run of the program there will be one <code>Foo</code> more in the database.<br />
How to deploy and run the program have a look at the last part of my <a href="http://bits-and-kites.blogspot.de/2014/01/spring-boot-example.html">previous post</a>.</p>Anonymoushttp://www.blogger.com/profile/17157288059906838258noreply@blogger.com0Berlin, Deutschland52.520006599999988 13.40495399999997552.210736099999991 12.759506999999974 52.829277099999985 14.050400999999976tag:blogger.com,1999:blog-9139661876943622135.post-47492224450089259052014-01-08T08:29:00.000-08:002015-05-13T11:04:42.953-07:00Spring Boot example<img src="http://projects.spring.io/spring-boot/img/project-icon-large.png" alt="MongoDB" class="img-thumbnail" style="max-width: 25%; float: left; margin-right: 30px;"/>
<p>In my <a href="http://bits-and-kites.blogspot.de/2013/07/simple-spring-annotation-configuration.html">first post</a> I introduced a simple skeleton for bootstraping a Spring application. With the new release of the <a href="http://projects.spring.io/spring-boot/" rel="" target="_blank">Spring Boot project</a> this one gets obsolete and starting a Spring application gets much easier.</p>
<p>As before, the complete source code is available on <a href="https://github.com/cnagel/spring-skeleton" target="_blank">GitHub</a>.</p>
<p>To use the new Spring libraries it needs to configure the Maven POM file (there is Gradle support on the Spring Boot project site too). Last time this was simply done by a small dependency, now it needs some more XML code.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.surpreso</groupId>
<artifactId>spring-skeleton</artifactId>
<version>0.0.3-SNAPSHOT</version>
<!-- The packaging format, use war for web projects -->
<packaging>jar</packaging>
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.1.RELEASE</version>
</parent>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project></pre>
</div>
<div class="panel-footer">pom.xml</div>
</div>
<p>The XML code doesn't look difficult, but lets go through it step for step. First we have to define the packaging format. As this application is no web project we will choose <code>jar</code>, otherwise <code>war</code>.</p>
<p>Now there comes the interesting part: Our project needs to inherite default values from the Spring Boot module to get well compiled. Running the project in an IDE worked without, but to build a jar containing all neccessary libraries you need to define the parent maven module.</p>
<p>Further more I added two dependencies to my project, starting with <code>spring-boot-starter</code>, which includes all Spring core libraries. To write unit and integration tests in a spring-fashion I added <code>spring-boot-starter-test</code> too.</p>
<p>The <code>spring-boot-maven-plugin</code> is needed to package all neccessary libraries into one uber-jar, which eases the deployment massively.</p>
<hr />
<p>Now lets come to the fun part of this tutorial. At first we define a Spring configuration class.<br />
Most Spring Boot tutorials skip this step and merge configuration and application class. Using our configurations in tests too, I seperated both.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_skeleton;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class DefaultConfig {
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_skeleton/DefaultConfig.java</div>
</div>
<p>The first annotation defines this class as a Spring configuration. The <code>@EnableAutoConfiguration</code> annotation enables the Spring Boot module to load default values and properties. With <code>@ComponentScan</code> we initiate Spring to search for auto wiring classes.</p>
<p>In the next step we create a very simple HelloWorld service which returns the current version of our application.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_skeleton;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class HelloWorldService {
@Value("${app.version}")
private String version;
/**
* @return the version
*/
public String getVersion() {
return version;
}
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_skeleton/HelloWorldService.java</div>
</div>
<p>The java code of the service class is very easy and there are only two spring relevant features used.<br />
The <code>@Compontent</code> annotation tells Spring to auto wire the class. For simplicity I didn't define a interface before, which would be the enterprise way.<br />
The <code>@Value</code> annotation introduces Spring to auto load the instance property <code>version</code> by the application property <code>app.version</code>. This property can be defined in a config file or by shell parameters.</p>
<p>In this project I choosed to read the properties from an YAML configuration file.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-yaml linenums pre-scrollable">
app:
version: 0.3</pre>
</div>
<div class="panel-footer">src/main/resources/application.yml</div>
</div>
<p>For different application properties like database access information you can define further configuration files by naming them <code>application-[profile].yml</code>.<br />
Before creating a test in the next steps, we define a <code>application-test.yml</code> configuration file to store a different version. Only being used in the test environment this file can use the <code>src/test/resources</code> folder.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-yaml linenums pre-scrollable">
app:
version: 0.3-test</pre>
</div>
<div class="panel-footer">src/test/resources/application-test.yml</div>
</div>
<p>The following test simply checks if the HelloWorld serice loads the correct version from the properties.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_skeleton;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationContextLoader;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.surpreso.spring_skeleton.DefaultConfig;
import com.surpreso.spring_skeleton.HelloWorldService;
@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DefaultConfig.class, loader = SpringApplicationContextLoader.class)
public class HelloWorldServiceTests {
@Autowired
HelloWorldService service;
@Test
public void test_getVersion() throws Exception {
assertTrue(service.getVersion().endsWith("-test"));
}
}</pre>
</div>
<div class="panel-footer">src/test/java/com/surpreso/spring_skeleton/HelloWorldServiceTests.java</div>
</div>
<p>The test class has three annotations. The first one defines the profile being used to load properties and classes. The <code>@RunWith</code> annotation instructs junit to use the Spring test runner. Last the <code>@ContextConfiguration</code> defines the configuration classes and which loader is used.<br />
The test itself is very straight forward, calls the <code>getVersion</code> method and checks if it ends with "-test". This should be true if the profile is loaded correctly.</p>
<p>Now we have tested our service, it is time to create an application which executes it. With Spring Boot this can be realized in a very elegant way.<p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_skeleton;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Import;
@Import(DefaultConfig.class)
public class HelloWorldApplication implements CommandLineRunner {
@Autowired
private HelloWorldService helloWorldService;
public static void main(String... args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
public void run(String... args) throws Exception {
LoggerFactory.getLogger(getClass()).info(
"This application works on version "
+ helloWorldService.getVersion());
}
}
</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_skeleton/HelloWorldApplication.java</div>
</div>
<p>Not much code as you can see. The class simply implements the <code>CommandLineRunner</code> interface and executes the <code>SpringApplication.run</code> method by passing itself as parameter.<br />
The <code>@Import(DefaultConfig.class)</code> annotation tells which configuration to us.<br />
After loading the Spring framework, the <code>run</code> method gets executed and logs the version of the application, being delivered by our impressive service.<br />
That's all! You can execute the application in your IDE or by maven and should see the Spring Boot splash output and the version of our project.</p>
<hr />
<p>The last part of this tutorial covers the deployment of our project. The packaging of all libraries in one jar isn't trivial but Spring Boot offers a nice Maven plugin. All we need to execute is the maven <code>package</code> goal by running <kbd>mvn package</kbd> in the shell. The resulting jar file in the target folder has a size of about 5MB and contains all classes and libraries. To execute the jar we need to run the following command: <kbd>java -jar target/spring-skeleton-0.0.3-SNAPSHOT.jar</kbd>.</p>
<p>But there exists a second way to start our little application the Spring Boot way. Just execute <kbd>mvn spring-boot:run</kbd> in the shell and the app is launched but not packaged.</p>
<p>OK, that's all for today. Thanks to the Spring Boot developers for this nice project.</p>Anonymoushttp://www.blogger.com/profile/17157288059906838258noreply@blogger.com2Berlin, Deutschland52.520006599999988 13.40495399999997552.210736099999991 12.759506999999974 52.829277099999985 14.050400999999976tag:blogger.com,1999:blog-9139661876943622135.post-42763687199224721252013-07-08T04:50:00.002-07:002014-03-24T06:03:55.150-07:00Simple Spring Annotation Configuration example<p>This is the start of a tutorial session about the Spring framework. In the next episodes I try to introduce lessons learnd at our project <a href="https://www.surpreso.com">Surpreso</a> which works on Spring and Ruby on Rails. While most of the tutorials work on XML-configuration, this lesson focusses on configuration by annotated classes.</p>
<p>Complete sourcecode is avaiable on <a href="https://github.com/cnagel/spring-skeleton/tree/v0.0.1" target="_blank">GitHub</a>.</p>
<p>We will start by configurate the maven POM to include the neccessary Spring libraries.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-xml linenums pre-scrollable">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.surpreso</groupId>
<artifactId>spring-skeleton</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Surpreso Spring skeleton</name>
<properties>
<spring.version>3.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<!-- Spring context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Google guava library -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>r09</version>
</dependency>
<!-- Apache Commons libraray -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
</project></pre>
</div>
<div class="panel-footer">pom.xml</div>
</div>
<p>The first dependency loads the Spring context module, the central component of Spring. Google Guava and Apache Commons are just added for syntactic sugar.</p>
<p>At next we will care on the main configuration for our program. Therefore a package called <code>config</code> gets created and we will annotate our main configuration class. First one defines the class as configuration, second one loads the properties file <code>application.properties</code> from the class path.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_skeleton.confg;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource({ "classpath:application.properties" })
public class MainConfig {
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_skeleton/confg/MainConfig.java</div>
</div>
<p>The <code>application.properties</code> file will be stored under <code>src/main/resources</code> in the maven way and simply holds a version information</p>
<strong>scr/main/resources/application.properties</strong>
<pre class="brush: java">
version = 0.1
</pre>
<p>For default jobs we will write a little helping abstract job class, which initializes the Spring context and loads the configurations.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_skeleton.jobs;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.surpreso.spring_skeleton.confg.MainConfig;
/**
* An abstract Job class, which helps to initialize the Spring context.
*
* @author Christoph Nagel
*/
abstract public class AbstractSpringJob {
static private ApplicationContext ctx;
/**
* Initiates the Spring context object by the given configurations. For
* default, {@link MainConfig} is used for bootstrapping properties.
*
* @param configurations
* An array of configuration classes
*/
static protected void init(Class<?>... configurations) {
// create the Spring context
ctx = new AnnotationConfigApplicationContext(
(Class<?>[]) ArrayUtils.add(configurations, MainConfig.class));
}
/**
* Getter for the Spring context
*
* @return The Spring context
*/
static protected ApplicationContext getContext() {
return ctx;
}
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_skeleton/jobs/AbstractSpringJob.java</div>
</div>
<p>Last but not least, a <code>HelloWorldJob</code> initializes the Spring context and prints the version of our first program.</p>
<div class="panel panel-default panel-code">
<div class="panel-body">
<pre class="prettyprint lang-java linenums pre-scrollable">
package com.surpreso.spring_skeleton.jobs;
/**
* A simple Hello World example how to initialize the Spring context-
*
* @author Christoph Nagel
*/
public class HelloWorldJob extends AbstractSpringJob {
public static void main(String... arg) {
// init the Spring context
init();
// do some job instructions
System.out.println("Version: "
+ getContext().getEnvironment().getProperty("version"));
}
}</pre>
</div>
<div class="panel-footer">src/main/java/com/surpreso/spring_skeleton/jobs/HelloWorldJob.java</div>
</div>
<p>That's all. Next steps will be testing, Spring profiles and Web MVC</p>Anonymoushttp://www.blogger.com/profile/17157288059906838258noreply@blogger.com0