tag:blogger.com,1999:blog-55617476955628565232024-03-07T20:04:35.057-08:00vikdorKrovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.comBlogger9125tag:blogger.com,1999:blog-5561747695562856523.post-17130743610862268532016-12-18T09:18:00.001-08:002016-12-18T14:00:01.637-08:00Angular2 + Webpack + Maven + SpringMVC + Swagger<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: left;">
This post is to track the steps followed to integrate an Angular2 web application with a Maven Spring MVC application, that uses Swagger annotations to annotate the Model and API objects and generate angular2-typescript code.</div>
<div style="text-align: left;">
<br /></div>
<h3 style="text-align: left;">
Glossary</h3>
<div>
<br />
<table>
<thead>
<tr>
<th>Term</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Angular2</b></td>
<td>Angular2 is a javascript framework to develop web applications for desktop and mobile. More about <a href="https://angular.io/docs/ts/latest/">Angular2 here</a>.
</td>
</tr>
<tr>
<td><b>Webpack</b></td>
<td>Webpack is a module bundler for javascript applications. More about <a href="https://webpack.js.org/concepts/">Webpack here</a>.
</td>
</tr>
<tr>
<td><b>Maven</b></td>
<td>Maven is a dependency management and build tool. More about <a href="http://maven.apache.org/">Maven here</a>.
</td>
</tr>
<tr>
<td><b>Spring MVC</b></td>
<td>Spring MVC provides a framework to dispatch HTTP requests to request handlers in a highly-configurable, extensible manner. More about <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html">Spring MVC here</a>.
</td>
</tr>
<tr>
<td><b>Swagger</b></td>
<td>Swagger is an API framework to document and consume RESTful APIs. More about <a href="http://swagger.io/tools/">Swagger here</a>.
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h2 style="text-align: left;">
Step-by-step Guide</h2>
<h3 style="text-align: left;">
Setup Angular2 Webapp</h3>
In a maven project, create the directory where angular2 code will be present:
<pre> <code class="language-bash" name="code">
# For example, in my-service
$ cd my-service/src/main
$ mkdir my-webapp
$ cd my-webapp
</code>
</pre>
Create <code>package.json</code> and add the following configuration (<i>Note: Replace all the ${webapp.*} placeholders in the file.</i>):
<pre>
<code class="language-javascript">
{
"name": "${webapp.name}",
"version": "${webapp.version}",
"description": "${webapp.description}",
"readme": "${webapp.readme.url}",
"repository": {
"type": "git",
"url": "${webapp.repository.url}"
},
"scripts": {
"start": "webpack-dev-server --inline --progress --port 8080",
"build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
},
"license": "ISC",
"dependencies": {
"@angular/common": "~2.2.0",
"@angular/compiler": "~2.2.0",
"@angular/core": "~2.2.0",
"@angular/forms": "~2.2.0",
"@angular/http": "~2.2.0",
"@angular/platform-browser": "~2.2.0",
"@angular/platform-browser-dynamic": "~2.2.0",
"@angular/platform-server": "~2.2.0",
"@angular/router": "~3.2.0",
"assets-webpack-plugin": "^3.4.0",
"bootstrap": "^3.3.6",
"core-js": "^2.4.1",
"font-awesome": "^4.6.3",
"moment": "^2.14.1",
"ng2-bootstrap": "1.1.16",
"ng2-modal": "0.0.22",
"rxjs": "5.0.0-beta.12",
"zone.js": "^0.6.25"
},
"devDependencies": {
"@types/core-js": "^0.9.35",
"@types/jasmine": "^2.5.35",
"@types/node": "^6.0.45",
"angular2-template-loader": "^0.4.0",
"awesome-typescript-loader": "^3.0.0-beta.13",
"connect-history-api-fallback": "^1.2.0",
"copy-webpack-plugin": "^4.0.0",
"css-loader": "^0.23.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5",
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.24.1",
"http-proxy-middleware": "^0.17.0",
"jasmine-core": "^2.4.1",
"null-loader": "^0.1.1",
"phantomjs-prebuilt": "^2.1.7",
"raw-loader": "^0.5.1",
"rimraf": "^2.5.4",
"script-ext-html-webpack-plugin": "^1.3.2",
"style-loader": "^0.13.1",
"to-string-loader": "^1.1.4",
"ts-helpers": "1.1.2",
"ts-node": "^1.7.0",
"typescript": "^2.1.4",
"webpack": "^1.13.3",
"webpack-dev-middleware": "^1.6.1",
"webpack-dev-server": "^1.16.2",
"webpack-md5-hash": "^0.0.5",
"webpack-merge": "^1.0.2"
}
}
</code>
</pre>
Create <code>tsconfig.json</code> and add the following configuration:
<pre>
<code class="language-json">
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
</code>
</pre>
Create <code>index.html</code>, the landing page for the webapp.
<pre>
<code class="language-html">
<!DOCTYPE html>
<html>
<head>
<title>My UI Application</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/static/ext/bootstrap/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<my-app>Loading My UI Application ...</my-app>
<script src="/static/ext/bootstrap/js/jquery.js"></script>
<script src="/static/ext/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
</code>
</pre>
Create <code>.npmrc</code> file with the following contents. This is needed while compiling the angular project.
<pre>
<code class="language-properties">
strict-ssl=false
registry=http://registry.npmjs.org/
</code>
</pre>
Add the <code>app</code> folder that actually contains all the modules, components, services corresponding to the webapp. Create client, components, service sub folders within the app folder. Create <code>app.module.ts</code> file in the app folder, with the following content:
<pre>
<code class="language-typescript">
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { Ng2BootstrapModule } from 'ng2-bootstrap/ng2-bootstrap';
import { RouterModule, PreloadAllModules } from '@angular/router';
import {APP_BASE_HREF, Location} from '@angular/common';
import { AppComponent } from './app.component';
@NgModule({
bootstrap: [AppComponent],
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
Ng2BootstrapModule,
RouterModule.forRoot([
])
],
providers: [
{
provide: APP_BASE_HREF,
useValue: window['_app_base'] || '/'
}
]
})
export class AppModule {
}
</code>
</pre>
Create <code>app.component.ts</code> with the following content:
<pre>
<code class="language-typescript">
import { Component, ViewContainerRef, OnDestroy } from '@angular/core';
@Component({
selector: 'my-app'
})
export class AppComponent {
}
</code>
</pre>
Distribute the dependencies into two different typescript files
<ul>
<li><code>polyfills.ts</code>: this will be loaded before the <body> tag in index.html.</li>
<li><code>vendor.ts</code>: this will be loaded before the </body> tag in index.html.</li>
</ul>
<pre>
<code class="language-typescript">
//polyfills.ts
import 'core-js/es6';
import 'core-js/es7/reflect';
require('zone.js/dist/zone');
if (process.env.ENV === 'production') {
// Production
} else {
// Development
Error['stackTraceLimit'] = Infinity;
require('zone.js/dist/long-stack-trace-zone');
}
</code>
</pre>
<pre>
<code class="language-typescript">
//vendor.ts
// Angular
import '@angular/platform-browser';
import '@angular/platform-browser-dynamic';
import '@angular/core';
import '@angular/common';
import '@angular/http';
import '@angular/router';
// RxJS
import 'rxjs';
// Other vendors for example jQuery, Lodash or Bootstrap
// You can import js, ts, css, sass, ...
</code>
</pre>
And <code>main.ts</code>:
<pre>
<code class="language-typescript">
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { AppModule } from './app.module';
if (process.env.ENV === 'production') {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
</code>
</pre>
Now the angular2 webapp directory will be as follows:
<pre>
<code class="language-textfile">
my-service
|- src
|- main
|- my-webapp
|- app
|- client -- This folder contains the swagger REST client that can be used to talk to the backend service.
|- model
|- <All model files>
|- components
|- <All component-specific typescript files, html files, css files etc.,>
|- service
|- <Service files corresponding to different components.>
|- app.module.ts -- Main module.
|- app.component.ts -- Top level component that will load the rest of the components.
|- main.ts -- entry point.
|- polyfills.ts
|- vendor.ts
|- .npmrc
|- index.html
|- package.json
|- tsconfig.json
</code>
</pre>
<h3>Configure Webpack</h3>
The <code>package.json</code> file created above already has the webpack dependencies included, and the scripts already are configured to run webpack command. Now, create <code>webpack.config.js</code> in the <code>webapp</code> root folder, i.e. in my-webapp.
<pre>
<code class="language-javascript">
module.exports = require('./config/webpack.dev.js');
</code>
</pre>
Create <code>config</code> directory under <code>my-webapp</code>. Add the following files:
<h4>helpers.js</h4>
<pre>
<code class="language-javascript">
var path = require('path');
var _root = path.resolve(__dirname, '..');
function root(args) {
args = Array.prototype.slice.call(arguments, 0);
return path.join.apply(path, [_root].concat(args));
}
exports.root = root;
</code>
</pre>
<h4>webpack.common.js</h4>
<pre>
<code class="language-javascript">
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');
const { CheckerPlugin } = require('awesome-typescript-loader')
module.exports = {
entry: {
'app': './app/main.ts',
'polyfills': './app/polyfills.ts',
'vendor': './app/vendor.ts'
},
resolve: {
extensions: ['', '.ts', '.js']
},
module: {
loaders: [
{
test: /\.ts$/,
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
},
{
test: /\.html$/,
loader: 'html'
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=assets/[name].[hash].[ext]'
},
{
test: /\.css$/,
exclude: helpers.root('app'),
loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
},
{
test: /\.css$/,
include: helpers.root('app'),
loader: 'raw'
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'polyfills', 'vendor']
}),
new HtmlWebpackPlugin({
template: './index.html',
baseUrl: '/'
}),
new CheckerPlugin()
]
};
</code>
</pre>
<h4>webpack.dev.js</h4>
<pre>
<code class="language-javascript">
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');
module.exports = webpackMerge(commonConfig, {
devtool: 'cheap-module-eval-source-map',
output: {
path: helpers.root('dist'),
publicPath: 'http://localhost:8080/',
filename: '[name].js',
chunkFilename: '[id].chunk.js'
},
plugins: [
new ExtractTextPlugin('[name].css')
],
devServer: {
historyApiFallback: {
index: "/",
baseUrl: "/"
},
stats: 'minimal',
proxy: [
{
context: [
<all paths that should be proxied to the backend server.>
],
target: '<URL of the backend server>',
secure: false
}
]
}
});
</code>
</pre>
<h4>webpack.prod.js</h4>
<pre>
<code class="language-javascript">
var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
module.exports = webpackMerge(commonConfig, {
devtool: 'source-map',
output: {
path: helpers.root('dist'),
publicPath: '/assets/',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].chunk.js'
},
htmlLoader: {
minimize: false // workaround for ng2
},
plugins: [
new webpack.NoErrorsPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
mangle: {
keep_fnames: true
}
}),
new ExtractTextPlugin('[name].[hash].css'),
new webpack.DefinePlugin({
'process.env': {
'ENV': JSON.stringify(ENV)
}
})
]
});
</code>
</pre>
At the end of this, the <code>my-webapp</code> folder would be as follows:
<pre>
<code class="language-textfile">
my-service
|- src
|- main
|- my-webapp
|- app
|- client -- This folder contains the swagger REST client that can be used to talk to the backend service.
|- model
|- <All model files>
|- components
|- <All component-specific typescript files, html files, css files etc.,>
|- service
|- <Service files corresponding to different components.>
|- app.module.ts -- Main module.
|- app.component.ts -- Top level component that will load the rest of the components.
|- main.ts -- entry point.
|- polyfills.ts
|- vendor.ts
|- config
|- helpers.js
|- webpack.common.js
|- webpack.dev.js
|- webpack.prod.js
|- .npmrc
|- index.html
|- package.json
|- tsconfig.json
</code>
</pre>
Now, the set up is ready to bring up the dev instance of the angular app with the following commands:
<pre>
<code class="language-bash">
# Run npm install, to install all the dependencies under the node_modules directory.
$ npm install
# A sample output would be as follows:
+-- @angular/common@2.2.4
+-- @angular/compiler@2.2.4
+-- @angular/core@2.2.4
+-- @angular/forms@2.2.4
+-- @angular/http@2.2.4
+-- @angular/platform-browser@2.2.4
+-- @angular/platform-browser-dynamic@2.2.4
+-- @angular/platform-server@2.2.4
| `-- parse5@2.2.3
+-- @angular/router@3.2.4
+-- @types/core-js@0.9.35
+-- @types/jasmine@2.5.38
+-- @types/node@6.0.51
+-- angular2-template-loader@0.4.0
| `-- loader-utils@0.2.16
| +-- big.js@3.1.3
| +-- emojis-list@2.1.0
| `-- json5@0.5.1
+-- assets-webpack-plugin@3.5.0
.....
.....
.....
.....
.....
+-- webpack-md5-hash@0.0.5
| `-- md5@2.2.1
| +-- charenc@0.0.1
| +-- crypt@0.0.1
| `-- is-buffer@1.1.4
+-- webpack-merge@1.1.1
| +-- lodash.clonedeep@4.5.0
| +-- lodash.differencewith@4.5.0
| +-- lodash.isequal@4.4.0
| +-- lodash.isfunction@3.0.8
| +-- lodash.isplainobject@4.0.6
| +-- lodash.mergewith@4.6.0
| `-- lodash.unionwith@4.6.0
`-- zone.js@0.6.26
npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.15
$
# Run npm start to start the webpack's dev server
$ npm start
# A sample output would be as follows:
[at-loader] Checking started in a separate process...
[at-loader] Ok, 0.874 sec. chunk {0} app.js (app) 959 kB {1} [rendered]
chunk {1} polyfills.js (polyfills) 246 kB {2} [rendered]
chunk {2} vendor.js (vendor) 2.6 MB [rendered]
Child html-webpack-plugin for "index.html":
chunk {0} index.html 542 bytes [rendered]
webpack: bundle is now VALID.
# Run npm run build to generate the artifacts that will be used when the angular webapp is hosted from the backend server
$ npm run build
< some output here...>
</code>
</pre>
Upon <code>npm run build</code>, <code>dist</code> directory will be created with the following files:
<pre>
<code class="language-textfile">
my-service
|- src
|- main
|- my-webapp
|- dist
|- app.*.js
|- app.*.js.map
|- index.html
|- pollyfills.*.js
|- pollyfills.*.js.map
|- vendor.*.js
|- vendor.*.js.map
</code>
</pre>
<h3>Spring MVC Configuration</h3>
As part of Spring MVC configuration, we will add the following:
<ol>
<li>Mapping /assets/* to the dist folder created above, that gets copied to WEB-INF/my-webapp/dist, more about that in the pom.xml section below.</li>
<li>Exclude /assets/* from Authentication.</li>
</ol>
<pre>
<code class="language-xml">
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/assets/**" />
</mvc:interceptor>
</mvc:interceptors>
<mvc:resources mapping="/assets/**" location="/WEB-INF/my-webapp/dist/" />
</code>
</pre>
In the Spring MVC controller, load the <code>index.html</code> file for all the paths starting with <code>"/ui"</code>, assuming that the Angular2 Webapp will be rendered at this path. If you would like to render the webapp at the root, then the controller should load index.html for <code>"", "/", "/**"</code>.
<pre>
<code class="language-java">
// If the angular2 webapp should be rendered in the root context.
@RequestMapping(value = { "", "/**" }, method = RequestMethod.GET)
// If the angular2 webapp should be rendered in the /ui context.
@RequestMapping(value = {"/ui", "/ui/**" }, method = RequestMethod.GET)
@ResponseBody
public String loadAngular2IndexHtml() throws IOException
{
return <The content of /WEB-INF/my-webapp/dist/index.html>;
}
</code>
</pre>
<h3>Maven Build Configuration - pom.xml</h3>
As part of pom.xml, we would like to
<ol>
<li>Compile and build the angular2 webapp using webpack</li>
<li>Copy the angular2 webapp artifacts generated by webpack's npm run build method, to WEB-INF directory, during the processing of preparing the war file.</li>
<li>Clean up the angular2 build artifacts as part of project clean up step.</li>
<li>(Optional) Generate the swagger client module for angular2 webapp.</li>
</ol>
<pre>
<code class="language-xml">
<properties>
<nodeVersion>v6.1.0</nodeVersion>
<npmVersion>3.8.6</npmVersion>
</properties>
<build>
<plugins>
<!-- Step 1: Compile and build the angular2 webapp -->
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<workingDirectory>src/main/my-webapp/</workingDirectory>
<installDirectory>target/temp</installDirectory>
</configuration>
<executions>
<!-- It will install nodejs and npm -->
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<workingDirectory>src/main/my-webapp/</workingDirectory>
<installDirectory>target/temp</installDirectory>
<nodeVersion>${nodeVersion}</nodeVersion>
<npmVersion>${npmVersion}</npmVersion>
</configuration>
</execution>
<!-- It will execute command "npm install" inside "/angular" directory -->
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<workingDirectory>src/main/my-webapp/</workingDirectory>
<installDirectory>target/temp</installDirectory>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>npm build</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<workingDirectory>src/main/my-webapp/</workingDirectory>
<installDirectory>target/temp</installDirectory>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
<!-- Step 2: Copy the angular2 build artifacts to the war generated for the backend webapp -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<webResources>
<resource>
<directory>src/main/my-webapp/</directory>
<targetPath>WEB-INF/my-webapp</targetPath>
<excludes>
<exclude>**/node_modules/**</exclude>
<exclude>**/node/**</exclude>
<exclude>**/typings/**</exclude>
</excludes>
</resource>
</webResources>
</configuration>
</plugin>
<!-- Step 3: Clean up the build artifacts -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<filesets>
<fileset>
<directory>src/main/my-webapp/dist</directory>
<includes>
<include>**/*</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
<fileset>
<directory>src/main/my-webapp/node_modules</directory>
<includes>
<include>**/*</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
<fileset>
<directory>src/main/my-webapp/node</directory>
<includes>
<include>**/*</include>
</includes>
<followSymlinks>false</followSymlinks>
</fileset>
</filesets>
</configuration>
</plugin>
<!-- Step 4: (Optional) Generate the Angular2-TypeScript client module for the backend REST services annotated with Swagger annotations.-->
<plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/webapp/my-service.swagger.json</inputSpec>
<language>typescript-angular2</language>
<output>src/main/my-webapp/app/client/</output>
<configOptions>
<supportsES6>true</supportsES6>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</code>
</pre>
</div>
Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-7166022646476674632015-10-27T20:17:00.000-07:002016-12-18T07:30:01.014-08:00Using SpringFox for API document generation.<div dir="ltr" style="text-align: left;" trbidi="on">
While working on <a href="http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/mvc.html">Spring MVC</a> based RESTful services that expose APIs for other developers to use, I needed a library that could read the API specifications in the <a href="http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/mvc.html#mvc-controller">Controller</a> classes, generate API documentation (so that it is always up-to-date with the changes to the API spec) and better if it can provide a way to invoke these APIs. <a href="http://springfox.github.io/springfox/docs/current/">Springfox</a>'s implementation of <a href="http://swagger.io/">Swagger</a> appeared to fit these requirements perfectly.<br />
<br />
This post is a log of changes I made to my Spring MVC RESTful service to integrate with <a href="http://springfox.github.io/springfox/docs/current/">Springfox</a>'s <a href="http://swagger.io/">Swagger</a> implementation.<br />
<ol style="text-align: left;">
<li>Included the maven dependencies for swagger-springfox to expose the API details and swagger-springfox-ui to render the documentation and provide a "Try it out" interface to invoke these APIs.</li>
<pre> <code class="language-xml" name="code">
<dependency>
<groupid>io.springfox</groupid>
<artifactid>springfox-swagger2</artifactid>
<version>2.2.2</version>
</dependency>
<dependency>
<groupid>io.springfox</groupid>
<artifactid>springfox-swagger-ui</artifactid>
<version>2.2.2</version>
</dependency>
</code>
</pre>
<li>Created a Configuration class to initialize SpringFox</li>
<pre >
<code class="language-java" name="code">import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.async.DeferredResult;
import com.fasterxml.classmate.TypeResolver;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig
{
@Autowired
private TypeResolver typeResolver;
@Bean
public Docket petApi()
{
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping("/")
.directModelSubstitute(LocalDate.class, String.class)
.genericModelSubstitutes(ResponseEntity.class)
.alternateTypeRules(
newRule(
typeResolver.resolve(
DeferredResult.class,
typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
typeResolver.resolve(WildcardType.class)))
.useDefaultResponseMessages(false)
.globalResponseMessage(
RequestMethod.GET,
newArrayList(
new ResponseMessageBuilder()
.code(500)
.message("500 message")
.responseModel(new ModelRef("Error"))
.build()))
.enableUrlTemplating(false);
}
}
</code>
</pre>
<li>With the addition of Springfox Configuration above, the test cases started failing due to missing ServletContext bean. Added the <code>@WebAppConfiguration</code> annotation to the parent test class that also has the test spring configuration initialized.</li>
<pre>
<code class="language-java" name="code">
import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-application-spring-config.xml" })
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
@WebAppConfiguration
@Ignore("Ingoring an abstract test class")
public abstract class AbstractIntegrationTest
{
}
</code>
</pre>
<li>Added the following resource mappings to serve the swagger related resources from springfox-swagger-ui.jar.</li>
<pre>
<code class="language-xml" name="code">
<mvc:resources location="classpath:/META-INF/resources/swagger-ui.html" mapping="swagger-ui.html"> <mvc:resources location="classpath:/META-INF/resources/webjars/" mapping="/webjars/**">
</mvc:resources></mvc:resources>
</code>
</pre>
</ol>
<div>
Notes:</div>
<div>
<ol style="text-align: left;">
<li>Initially, I set the "enableUrlTemplating" attribute to "true" in SwaggerConfiguration. Due to this the URLs in "Try it out" section included placeholders for the request parameters, making this feature use less.<br /><br />E.g. <br />URL when the attribute is set to true:<br /> http://localhost:8080/my-api{?param1,param2}?param1=&param2=&<br /><br />URL after the attribute value is changed to false:<br /> http://localhost:8080/my-api?param1=&param2=&</li>
<li>At first, I included the mvc resource mappings in the SwaggerConfiguration class itself, by extending the WebMvcConfigurerAdapter class, but access to swagger-ui.html returned 404 error. So, I removed this from here and added them to the XML configuration and then the swagger-ui.html came up properly.</li>
<pre><code class="language-java" name="code"> @Override
public void addResourceHandlers(final ResourceHandlerRegistry registry)
{
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
</code>
</pre>
<li>I tried to render swagger-ui.html under a different URL (e.g. /my-service-api) but, due to hard coded lookup for swagger-ui.html in the springfox.js, even though the page loaded, it couldn't proceed further to make call to the /v2/api-docs to get the JSON and render it.</li>
</ol>
</div>
</div>
Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-11831129781735445962014-12-21T22:59:00.001-08:002016-12-18T07:32:27.037-08:00Configuring Log4J with Slf4J in Spring-based application<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
While working with a <a href="http://projects.spring.io/spring-boot/">Spring Boot</a> based application, the spring-boot jar brings in the <a href="http://www.slf4j.org/">sl4j</a> related jars which seems to be configured to <a href="http://commons.apache.org/proper/commons-logging/">jakarta-commons-logging</a> by default. So, the log4j.properties in the classpath was never read by the <a href="http://logging.apache.org/">log4j</a> system.<br />
<br />
To make <a href="http://logging.apache.org/">log4j</a> work with <a href="http://www.slf4j.org/">sl4j</a> in this scenario, the <a href="http://www.slf4j.org/">sl4j</a> dependencies coming from spring-boot have been excluded as follows:<br />
<br /></div>
<pre>
<code class="language-xml" name="code">
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-rest</artifactid>
<version>${spring-boot-version}</version>
<exclusions>
<exclusion>
<groupid>org.slf4j</groupid>
<artifactid>slf4j-api</artifactid>
</exclusion>
<exclusion>
<groupid>org.slf4j</groupid>
<artifactid>jul-to-slf4j</artifactid>
</exclusion>
<exclusion>
<groupid>org.slf4j</groupid>
<artifactid>log4j-over-slf4j</artifactid>
</exclusion>
<exclusion>
<groupid>org.slf4j</groupid>
<artifactid>jcl-over-slf4j</artifactid>
</exclusion>
<exclusion>
<groupid>ch.qos.logback</groupid>
<artifactid>logback-classic</artifactid>
</exclusion>
</exclusions>
</dependency>
</code>
</pre>
Then, the <a href="http://www.slf4j.org/">sl4j</a> and <a href="http://logging.apache.org">log4j</a> dependencies are pulled in separately, as follows:
<pre><code class="language-xml" name="code">
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>runtime</scope>
</dependency>
</code>
</pre>
The <a href="http://projects.spring.io/spring-framework/">Spring Test</a> library seems to require JCL (jakarta-commons-logging) as it was throwing the following exception:
<pre><code class="language-java" name="code">
java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.clinit(SpringJUnit4ClassRunner.java:86)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
</code>
</pre>
Included the <code>jcl-over-slf4j</code> dependency to fix the above exception:
<pre> <code class="language-xml" name="code">
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
</code>
</pre>
Now, the log4j.properties file in <code>src/main/resources</code> is read by the log4j system, without any additional configuration.
</div>Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-56638540675289837942013-01-15T20:09:00.000-08:002016-12-18T07:33:53.061-08:00Functional testing in SOA environment<div dir="ltr" style="text-align: left;" trbidi="on">
My first project in a service oriented environment has reached <a href="http://en.wikipedia.org/wiki/Functional_testing">functional testing</a> stage. The product involves processing large amounts of business objects whose reference data is obtained from other subsystems through service calls, in production.<br />
<br />
While devising the strategy for functional testing, the issue was how to get this reference data in a test environment.
<br /><br />
<h3>Unit Testing</h3>
During unit testing, the service calls were mocked using a mocking library (<a href="http://code.google.com/p/mockito/">Mockito</a> in our case), and the expected responses were returned using <i>when - thenReturn</i> construct, as follows:<br />
<br />
<pre> <code class="language-java">
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.mockito.MockitoAnnotations;
import org.mockito.Mock;
import org.junit.Before;
import org.junit.Test;
public class SweepTest
{
/*
* Specify that the service wrapper should be mocked.
*/
@Mock
private XServiceWrapper xServiceWrapper;
/*
* SomeBusinessObjectManager has reference to XServiceWrapper and we want the
* mocked instance to be injected instead of the normal implementation.
*/
@InjectMocks
@Autowired
private SomeBusinessObjectManager boManager;
@Before
public void setup()
{
MockitoAnnotations.initMocks(this);
/*
* Mock the response object from the service.
*/
XServiceResponse serviceResponse = mock(XServiceResponse.class);
when(serviceResponse.getXValue()).thenReturn(myValue);
/*
* Mock the service call and return mocked response object.
*/
when(xServiceWrapper.serviceCall()).thenReturn(serviceResponse);
}
}
</code>
</pre>
<br />
<h3>Functional Testing</h3>
During functional testing, we identified golden set of use cases that necessarily and sufficiently cover the behavior of the system in various scenarios. The data for various internal objects for these golden set of use cases is prepared. But, it is not feasible to get relevant data from the external services that would suit the input data for a given use case. So, the following changes were done to the project w.r.t interacting with external services:
<OL>
<LI>Define an <code>ExternalServiceFacade</code> interface that would define all the calls to be made to different external services.</LI>
<pre><code class="language-java">
/**
* ExternalServiceFacade.java
*
* $Source$
*/
/*
* Copyright (c) 2013 Krovi.com, Inc. All rights reserved.
*/
package com.krovi.app.manager;
import com.krovi.services.BusinessObjectA;
import com.krovi.services.BusinessObjectB;
import com.krovi.services.BusinessObjectC;
/**
* This class follows the <a href="http://en.wikipedia.org/wiki/Facade_pattern">Facade pattern</a> and provides APIs to
* interact with various external services that provide reference data for the given object IDs.
*
* @author krovi
* @version $Id$
*/
public interface ExternalServiceFacade
{
BusinessObjectA lookupBusinessObjectA(String referenceId);
BusinessObjectB lookupBusinessObjectB(String referenceId1, String referenceId2);
BusinessObjectC lookupBusinessObjectC(String referenceId1, String referenceId2, Long referenceValue3);
}
</code>
</pre>
<LI>Provide an implementation that delegates the calls to the actual services and obtains the responses. This implementation will be used in production scenarios.</LI>
<pre><code class="language-java">
/**
* ExternalServiceFacadeImpl.java
*
* $Source$
*/
/*
* Copyright (c) 2013 Krovi.com, Inc. All rights reserved.
*/
package com.krovi.app.manager;
import com.krovi.services.BusinessObjectA;
import com.krovi.services.BusinessObjectAService;
import com.krovi.services.BusinessObjectAServiceClient;
import com.krovi.services.BusinessObjectB;
import com.krovi.services.BusinessObjectBService;
import com.krovi.services.BusinessObjectBServiceClient;
import com.krovi.services.BusinessObjectC;
import com.krovi.services.BusinessObjectCService;
import com.krovi.services.BusinessObjectCServiceClient;
/**
* @author krovi
* @version $Id$
*/
public class ExternalServiceFacadeImpl implements ExternalServiceFacade
{
@Autowired
private BusinessObjectAService boAService;
@Autowired
private BusinessObjectBService boBService;
@Autowired
private BusinessObjectCService boCService;
@Override
public BusinessObjectA lookupBusinessObjectA(String referenceId)
{
BusinessObjectAServiceClient client = boAService.getClient();
return client.lookupBusinessObjectA(referenceId);
}
@Override
public BusinessObjectB lookupBusinessObjectB(String referenceId1, String referenceId2)
{
BusinessObjectBServiceClient client = boBService.getClient();
return client.lookupBusinessObjectB(referenceId1, referenceId2);
}
@Override
public BusinessObjectC lookupBusinessObjectC(String referenceId1, String referenceId2, Long referenceValue3)
{
BusinessObjectCServiceClient client = boCService.getClient();
return client.lookupBusinessObjectC(referenceId1, referenceId2, referenceValue3);
}
}
</code>
</pre>
<LI>Provide a mock implementation that will connect to a local data store that is populated with the expected data and returns it. This implementation will be used in functional testing scenario.</LI>
<pre> <code class="language-java">
/**
* MockExternalServiceFacadeImpl.java
*
* $Source$
*/
/*
* Copyright (c) 2013 Krovi.com, Inc. All rights reserved.
*/
package com.krovi.app.manager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import com.krovi.services.BusinessObjectA;
import com.krovi.services.BusinessObjectB;
import com.krovi.services.BusinessObjectC;
/**
* @author krovi
* @version $Id$
*/
public class MockExternalServiceFacadeImpl implements ExternalServiceFacade
{
@Autowired
private JdbcTemplate jdbcTemplate;
public MockExternalServiceFacadeImpl()
{
setupDBObjects();
}
private void setupDBObjects()
{
/**
* 1. This method creates DB tables corresponding to the structures of BusinessObject* classes. 2. The test
* harness would insert test data into these tables as per the use cases.
*/
}
@Override
public BusinessObjectA lookupBusinessObjectA(String referenceId)
{
// Look up the database instead of making a service call. The database would contain the appropriate object
// required for the test case that is being currently run.
return null;
}
@Override
public BusinessObjectB lookupBusinessObjectB(String referenceId1, String referenceId2)
{
// Look up the database instead of making a service call. The database would contain the appropriate object
// required for the test case that is being currently run.
return null;
}
@Override
public BusinessObjectC lookupBusinessObjectC(String referenceId1, String referenceId2, Long referenceValue3)
{
// Look up the database instead of making a service call. The database would contain the appropriate object
// required for the test case that is being currently run.
return null;
}
}
</code>
</pre>
<LI>Modify all calls to services in the entire code base to go through <code>ExternalServiceFacade</code>.</LI>
<LI>The spring configuration files for production and testing stages are modified to inject the appropriate implementation of the ExternalServiceFacade.
</LI>
</OL>
</div>
Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-65921503233904476162012-10-17T02:12:00.001-07:002016-12-18T07:35:06.343-08:00@Memoize in Java using Spring and Aspects<div dir="ltr" style="text-align: left;" trbidi="on">
Recently, while writing an application in service oriented architecture, I had a couple of places in the code where I needed to make a service call to get the details by a particular application key. I was wondering about duplicate calls, i.e. multiple service calls made to get the details for the same application key. As we all know, service calls are costly with all the marshalling, unmarshalling, cache lookups, database lookups involved, I wanted to cache the results of a service call by application key or keys and this is a clear cross-cutting concern. Having programmed in Perl and Python that have the <a href="http://en.wikipedia.org/wiki/Memoization">memoize</a> option, I thought a Memoize aspect would be most appropriate here. As always, I was looking for <a href="http://static.springsource.org/spring/docs/3.0.x/reference/aop.html">Spring's Aspect Oriented Programming</a> APIs and though it was tricky to get the set up right, I ended up with a very small amount of code that does the basic memoization. Since the set up was tricky, I thought I would document here for reference:<br />
<br />
<br />
<ol style="text-align: left;">
<li>Set up <a href="http://www.springsource.org/">Spring</a>, <a href="http://www.eclipse.org/aspectj/">AspectJ</a>, <a href="http://cglib.sourceforge.net/">CGLib</a> and <a href="http://asm.ow2.org/index.html">ASM</a> for the application.</li>
<ol>
<li>The following Spring jars are needed:</li>
<ol>
<li>spring-aop (v3.3.1)</li>
<li>spring-aspects (v3.3.1)</li>
<li>spring-beans (v3.3.1)</li>
<li>spring-context (v3.3.1)</li>
<li>spring-context-support (v3.3.1)</li>
<li>spring-core (v3.3.1)</li>
<li>spring-test (v3.3.1)</li>
<li>spring-expression (v3.3.1)</li>
<li>spring-asm (v3.3.1)</li>
</ol>
<li>The following AspectJ jars are needed for load-time-weaving and aspectj runtime.</li>
<ol>
<li>aspectj-rt (v1.6.9)</li>
<li>aspectj-weaver (v1.5.4)</li>
<li>aop-alliance (v1.0)</li>
</ol>
<li>The following CGLib jars are needed for code generation</li>
<ol>
<li>cglib (v2.2.3)</li>
</ol>
<li>The following ASM jars are needed for byte-code manipulation</li>
<ol>
<li>asm (v3.3.1)</li>
</ol>
<li>Commons logging jar needed by Spring</li>
<ol>
<li>commons-logging (v1.1.1)</li>
</ol>
</ol>
<li>The CGLib and ASM libraries should be compatible with each other. So, if you choose a different version of any of these jars, make sure to select the compatible other one. Otherwise, you will end up with a runtime error (NoSuchMethodError).</li>
<li>Enable aspects in your spring application using the following configuration:
<pre><code class="language-xml">
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
<context:component-scan base-package="com.vikdor.aspects" />
<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>
</code>
</pre>
</li>
<li>Define the annotation that is going to be used in the advice:
<pre><code class="language-java">
import java.lang.annotation.Retention;
@Retention(RetentionPolicy.RUNTIME)
public @interface Memoize
{
}
</code>
</pre>
</li>
<li>Write a simple Aspect class, annotate it with @Aspect and define an @Around advice for all the methods that use the @Memoize annotation, as follows:
<pre><code class="language-java">
@Aspect
@Component
public class MemoizeAspect
{
private WeakHashMap<Object[], Object> cache = new WeakHashMap<Object[], Object>();
@Around("@annotation(com.vikdor.aspects.Memoize)")
public Object handledMemoize(ProceedingJoinPoint pjp) throws Throwable
{
System.out.println("Handling memoize");
Object[] args = pjp.getArgs();
if (args != null)
{
Object response = cache.get(Arrays.asList(args));
if (response != null)
{
return response;
}
}
Object obj = pjp.proceed();
if (args != null)
{
cache.put(Arrays.asList(args), obj);
}
return obj;
}
}
</code>
</pre>
</li>
</ol>
Now, add @Memoize to any method whose request and response should be cached and it just works. BTW, this is a very naive Memoize implementation and is not production ready at all ;-)
</div>Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-23918220249732727212012-10-07T20:33:00.001-07:002016-12-18T07:35:44.758-08:00Connecting to SQL Server Express through JDBC<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
I came across this question in a decent number of forums where the subtleties of connecting to an instance of <a href="http://www.microsoft.com/en-us/download/details.aspx?id=29062">SQL Server Express</a> using JDBC are missed out. So, thought it would be good to just document how I approached it, here for my reference and for others, if they find useful.<br />
Once you install the SQL Server Express edition, you need to configure the TCP/IP settings for the JDBC drivers to connect to the SQLEXPRESS database instance.<br />
<br />
<b>Configuring the TCP/IP:</b><br />
<br />
<ol style="text-align: left;">
<li>Configure TCP/IP communication with SQL Express</li>
<ol>
<li>Open SQL Server Configuration Manager.</li>
<li>Go to SQL Server Network Configuration -> Protocols for SQLEXPRESS</li>
<li>Set the status of TCP/IP protocol to "Enabled" (if it is already not).</li>
<li>Open Properties window for TCP/IP, go to IP Addresses section.</li>
<li>Go to the bottom of this property page and set the TCP Port under `IPAll` to 1433.</li>
</ol>
<li>Connect to the SQLExpress instance using `Microsoft's JDBC driver for SQL Server`</li>
<ol>
<li>JDBC URL: `jdbc:sqlserver://localhost;instance=SQLEXPRESS;databaseName=<your DB>;user=<your User>;password=<your Passwd>`</li>
</ol>
</ol>
<br />
<div>
<b>Using Windows Integrated Authentication:</b></div>
<ol style="text-align: left;">
<li>Create a windows account for the application that would be used to run your programs. This account's credentials will be used to connect to the server.</li>
<li>Get Microsoft JDBC Driver for SQL Server from <a href="http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11774">here</a>.</li>
<li>Configure the JDBC URL as follows:<br /><br /><span style="background-color: #eeeeee;">jdbc:sqlserver://<hostname>;databaseName=<DBName>;integratedSecurity=true</span></li>
<li>Configure the launcher that run the Java programs from command line to include the following JVM parameter:<br /><br /><span style="background-color: #f3f3f3;">-Djava.library.path="<jdbc driver dll location>"</span><br /><br />where the location is the directory where the JDBC driver downloaded earlier is installed or extracted. It was <span style="background-color: #eeeeee;">C:\Program Files\sqljdbc_4.0.2206.100_enu\sqljdbc_4.0\enu\auth\x64</span> in my case. The dll should be picked based on the JVM used for running these programs.</li>
</ol>
<br />
With the above configuration, the connection established to SQL Server would use the Windows Authentication Credentials of the domain user running the java program/process.<br />
<div>
<br />
<b>Sample Code:</b></div>
<div>
<br /></div>
</div>
<pre> <code class="language-java">
public class SQLServerEx
{
static enum Drivers
{
MICROSOFT(
SQLServerDriver.class.getName(),
"jdbc:sqlserver://"
+ "localhost;"
+ "instance=SQLEXPRESS;databaseName=vikdor_test;user=vikdor_user;password=vikdor_user"),
MICROSOFT_INTEG(
SQLServerDriver.class.getName(),
"jdbc:sqlserver://"
+ "localhost;"
+ "instance=SQLEXPRESS;databaseName=vikdor_test;integratedSecurity=true");
private String driverClass;
private String driverURL;
private Drivers(String driverClass, String driverURL)
{
this.driverClass = driverClass;
this.driverURL = driverURL;
}
public String getDriverClass()
{
return driverClass;
}
public String getDriverURL()
{
return driverURL;
}
}
public static void main(String[] args) throws Exception
{
Class.forName(Drivers.MICROSOFT_INTEG.getDriverClass());
Connection con =
DriverManager.getConnection(Drivers.MICROSOFT_INTEG.driverURL);
PreparedStatement ps =
con.prepareStatement(
"insert into auto_table(name, role) values(?, ?)",
Statement.RETURN_GENERATED_KEYS);
ps.setString(1, "vikdor");
ps.setString(2, "sde");
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
while (rs.next())
{
System.out.println(rs.getInt(1));
}
rs.close();
ps.close();
con.close();
}
}
</code>
</pre>
</div>
Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-1319857949533294292012-10-06T21:39:00.000-07:002012-10-06T21:46:34.260-07:00Using Collection Merging in Spring to re-use a list<div dir="ltr" style="text-align: left;" trbidi="on">
Recently, I came across a requirement where I had a set of rules to be run on various sub-types of a business object--Invoice. All sub-types shared a common set of rules and then each sub-type has some exclusive set of rules to be run along with those in the common set. I was using Spring for dependency injection and did it as follows, initially:<br />
<br />
<pre name="code" class="xml">
<bean id="rule1" class="com.vikdor.rules.Rule1" />
<bean id="rule2" class="com.vikdor.rules.Rule2" />
<bean id="rule3" class="com.vikdor.rules.Rule3" />
<bean id="rule4" class="com.vikdor.rules.Rule4" />
<bean id="rule5" class="com.vikdor.rules.Rule5" />
<util:list id="normalInvRules">
<ref bean="rule1" />
<ref bean="rule3" />
<ref bean="rule5" />
<ref bean="rule4" />
</util:list>
<util:list id="prepaidInvRules">
<ref bean="rule1" />
<ref bean="rule3" />
<ref bean="rule5" />
<ref bean="rule2" />
</util:list>
</pre>
</div>
This obviously was not going to scale in my use case where I have good number of rules to be run and the number of sub-types are also more. I was wondering if there is a way to define the common list and then re-use it while defining the actual list of rules for a given type of invoice and then I came across <a href="http://static.springsource.org/spring/docs/3.1.2.RELEASE/spring-framework-reference/html/beans.html#beans-collection-elements" target="_blank">Collection Merging</a> concept in Spring where we can define a <b>parent</b> element and then have child elements inherit the properties from the parent element and override them (i.e. when there is a conflict, the child property definition takes precedence) and modified my configuration as follows and it works great!
<pre name="code" class="xml">
<bean id="rule1" class="com.krovi.rules.Rule1" />
<bean id="rule2" class="com.krovi.rules.Rule2" />
<bean id="rule3" class="com.krovi.rules.Rule3" />
<bean id="rule4" class="com.krovi.rules.Rule4" />
<bean id="rule5" class="com.krovi.rules.Rule5" />
<bean id="commonList"
class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list>
<ref bean="rule1" />
<ref bean="rule2" />
<ref bean="rule3" />
</list>
</property>
</bean>
<bean id="firstExecutorRules" parent="commonList"
class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list merge="true">
<ref bean="rule4" />
</list>
</property>
</bean>
<bean id="secondExecutorRules" parent="commonList"
class="org.springframework.beans.factory.config.ListFactoryBean">
<property name="sourceList">
<list merge="true">
<ref bean="rule5" />
</list>
</property>
</bean>
</pre>Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-64065821596563352762012-08-14T00:22:00.000-07:002014-01-21T12:05:24.406-08:00Using @Configurable to inject resources into objects created using "new" operator (domain objects)<div dir="ltr" style="text-align: left;" trbidi="on">
I am currently developing a query language based on a context-free grammar. The library contains a parser that parses the queries accepted by the language and returns a tree of parse entries. So, these parse entries essentially form the domain objects of the application. The library also provides the evaluator framework to evaluate the state of an object against a parse tree. A parse entry contains a reference to the evaluator that should be used to evaluate the state of an object against itself.
<br />
Since the parse entries are created using the <code>new</code> operator during the parse process and since it is not a good idea to hard-wire the evaluators into the parse entry classes, I was looking for a way to inject them. Then I came across the <a href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable" target="_blank">@Configurable</a> annotation provided by Spring framework to inject beans into objects of a class whose objects are instantiated using the <code>new</code> operator instead of getting them from the Spring framework. I followed the <b>Load Time Weaving</b> approach to get this working, in a sample project, as follows:
<br />
<ol>
<li>Configure the maven repositories required:
<pre class="xml" name="code"> <!-- This repository is needed for the aspectj-weaver dependency -->
<repository>
<id>com.springsource.repository.bundles.external</id>
<name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
<url>http://repository.springsource.com/maven/bundles/external</url>
</repository>
</pre>
</li>
<li>Configure the maven dependencies required:
<pre class="xml" name="code"><dependencies>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-aspects</artifactid>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-context</artifactid>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-test</artifactid>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupid>org.aspectj</groupid>
<artifactid>com.springsource.org.aspectj.weaver</artifactid>
<version>1.6.12.RELEASE</version>
</dependency>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.10</version>
</dependency>
</dependencies>
</pre>
</li>
<li>Created a sample domain object--<code>Account.java</code>, mark it as Configurable, and add a reference to the yet to be created AccountDAO.
<pre class="java" name="code">@Configurable
public class Account
{
protected AccountDAO accountDAO;
private int accountId;
private String accountHolderName;
private double balance;
public Account(int accountId, String holderName, double balance)
{
this.accountId = accountId;
this.accountHolderName = holderName;
this.balance = balance;
}
public AccountDAO getAccountDAO()
{
return accountDAO;
}
/**
* @return the accountId
*/
public int getAccountId()
{
return accountId;
}
/**
* @return the accountHolderName
*/
public String getAccountHolderName()
{
return accountHolderName;
}
/**
* @return the balance
*/
public double getBalance()
{
return balance;
}
public void setAccountDAO(AccountDAO accountDAO)
{
this.accountDAO = accountDAO;
}
/**
* @param accountId the accountId to set
*/
protected void setAccountId(int accountId)
{
this.accountId = accountId;
}
/**
* @param accountHolderName the accountHolderName to set
*/
protected void setAccountHolderName(String accountHolderName)
{
this.accountHolderName = accountHolderName;
}
/**
* @param balance the balance to set
*/
protected void setBalance(double balance)
{
this.balance = balance;
}
public void persist()
{
}
}
</pre>
<b>The setter method for <code>accountDAO</code> must be public for Spring to find it and inject it</b><i>(I found this strange as I thought the reflection based frameworks can access protected methods as well, which is the case with Hibernate.)</i>
</li>
<li>Create the DAO and sample DAOImpl for the <code>Account</code> class.
<pre class="java" name="code">public interface AccountDAO
{
public Account createAccount(Account account);
public Account updateAccount(Account account);
public void delete(Account account);
}
</pre>
<pre class="java" name="code">public class AccountDAOImpl implements AccountDAO
{
public Account createAccount(Account account)
{
System.out.println("Created....");
return account;
}
public Account updateAccount(Account account)
{
System.out.println("Update....");
return account;
}
public void delete(Account account)
{
System.out.println("Deleted....");
}
}
</pre>
</li>
<li>The application-config.xml file:
<pre class="xml" name="code">
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:annotation-config />
<context:spring-configured />
<context:load-time-weaver />
</beans>
</pre>
</li>
<li>Write a sample test class <code>TestAccount</code>
<pre class="java" name="code">@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations =
{ "file:META-INF/application-config.xml" })
public class TestAccount
{
@Test
public void test()
{
Account account = new Account(-1, "Krovi Sarma", 1630000.00);
assertNotNull(account.getAccountDAO());
}
}
</pre>
</li>
<li>Run the test class with the JVM argument <code>-javaagent:/path/to/org.springframework.instrument.jar</code>
</li>
</ol>
</div>Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0tag:blogger.com,1999:blog-5561747695562856523.post-35504288431752381682012-08-13T05:25:00.000-07:002012-08-13T05:25:31.790-07:00Première Poste<div dir="ltr" style="text-align: left;" trbidi="on">
This is the first post on blog, mostly to track things related to data structures, java application development etc.,</div>
Krovi Sarmahttp://www.blogger.com/profile/00323288434318864651noreply@blogger.com0