I recently used maven-jaxb2-plugin to generate java classes based on xml schemas (xsd). I can run "mvn compile" on command line to generate java classes with the following configuration:
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
However, my Eclipse shows the following error:
Plugin execution not covered by lifecycle configuration: org.jvnet.jaxb2.maven2:maven-jaxb2-plugin:0.8.1:generate (execution: default, phase: generate-sources)
This error prevents Eclipse generating java source codes and adding the generated sources to the class path. In order to fix this problem, I need to install m2e connector for maven-jaxb2-plugin. I can manually install it to Eclipse with the following update site:
http://bitstrings.github.com/m2e-connectors-p2/milestones/
However, Eclipse provides a better to install it. Eclipse shows the problem with the following:
Roll the mouse over <execution>, it displays the following:
Click the link "Discover new m2e connector", and eclipse will try to find the correct connector automatically:
Eclipse found the same m2e connector. Click finish to install it. After I installed it successfully, Eclipse is happy with maven-jaxb2-plugin.
This approach can be applied to other plugins too.
Search This Blog
Sunday, June 24, 2012
Friday, June 8, 2012
Enable CORS support in REST services with Spring 3.1
We are working on a web project, which has 2 components, UI and web services. These 2 components are deployed to 2 different servers. UI uses jQuery to make ajax call to back end restful web services. Back end web services use Spring 3.1 MVC framework. We are facing the XSS (cross site scripting) issue since modern browsers don't allow XSS.
The back end rest web services support HTTP methods GET, POST, PUT and DELETE. The first problem we solved was how to call HTTP method GET from UI using jQuery. Since I used JSONP before, it is a natural solution to me to use JSONP again. My coworker already blog the solution here: http://www.iceycake.com/2012/06/xml-json-jsonp-web-service-endpoints-spring-3-1.
However, JSONP only supports GET, and you can't set http headers with JSONP request. I also realized that JSONP is kind of out-of-dated too. So how to support other methods like PUT, POST?
CORS, Cross-Origin Resource Sharing, defines a mechanism to enable client-side cross-origin requests. CORS are widely supported by modern browsers like FireFox, Chrome, Safari, and IE.
The UI code is fairly simple:
The back end rest web services support HTTP methods GET, POST, PUT and DELETE. The first problem we solved was how to call HTTP method GET from UI using jQuery. Since I used JSONP before, it is a natural solution to me to use JSONP again. My coworker already blog the solution here: http://www.iceycake.com/2012/06/xml-json-jsonp-web-service-endpoints-spring-3-1.
However, JSONP only supports GET, and you can't set http headers with JSONP request. I also realized that JSONP is kind of out-of-dated too. So how to support other methods like PUT, POST?
CORS, Cross-Origin Resource Sharing, defines a mechanism to enable client-side cross-origin requests. CORS are widely supported by modern browsers like FireFox, Chrome, Safari, and IE.
The UI code is fairly simple:
<html>If you just run the above javascript and call the backend, you may notice that the PUT request is changed to OPTIONS. The browser sends an OPTIONS request to the server, and the server needs to send response with correct headers. So in order to return the correct response, a filter is created (thanks to the link https://gist.github.com/2232095):
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="application/javascript">
(function($) {
var url = 'http://localhost:8080/employee/id';
$.ajax({
type: 'put',
url: url,
async: true,
contentType: 'application/json',
data: '{"id": 1, "name": "John Doe"}',
success: function(response) {
alert("success");
},
error: function(xhr) {
alert('Error! Status = ' + xhr.status + " Message = " + xhr.statusText);
}
});
})(jQuery);
</script>
</head>
<body>
<!-- we will add our HTML content here -->
</body>
</html>
package com.zhentao;
import java.io.IOException;The web.xml needs adding the following too:
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
public class CorsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (request.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(request.getMethod())) {
// CORS "pre-flight" request
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
response.addHeader("Access-Control-Max-Age", "1800");//30 min
}
filterChain.doFilter(request, response);
}
}
<filter>However, this isn't enough yet. The original post didn't address it. You still need to change your rest web services to return Accept-Controll-Allow-Origin header since HTTP is stateless. Here is what I did with Spring Rest api:
<filter-name>cors</filter-name>
<filter-class>com.zhentao.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
@RequestMapping(value = "/employee/{id}", method = RequestMethod.PUT, consumes = {"application/json"})
public ResponseEntity<Employee> create(@Valid @RequestBody Employee employee, @PathVariable String id)
employee.setId(id);//some logic here
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Allow-Origin", "*");
ResponseEntity<Employee> entity = new ResponseEntity<Employee>(headers, HttpStatus.CREATED);
return entity;
}Update: it seems my post caused some confusion, and I created another post and fully working examples for CORS. See here.
Wednesday, May 30, 2012
log4j filter some text and don't log it
Use org.apache.log4j.varia.StringMatchFilter to filter the text not to display. Here is the example for log4j.xml:
LOG.info("This is an info and shouldn't be logged with the-text-not-to-log and blah blah");
LOG.info("This is logged");
LOG.debug("This is a debug and not logged");
LOG.error("This is an error and logged");
If you only want to log some text, then change the value for AcceptOnMatch to true and add DenyAllFilter.
<?xml version="1.0" encoding="UTF-8" ?>Then run the following to test:
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p - %m%n" />
</layout>
<filter class="org.apache.log4j.varia.StringMatchFilter">
<param name="StringToMatch" value="the-text-not-to-log" />
<param name="AcceptOnMatch" value="false" />
</filter>
<!-- <filter class="org.apache.log4j.varia.DenyAllFilter" /> -->
</appender>
<root>
<priority value="info" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
LOG.info("This is an info and shouldn't be logged with the-text-not-to-log and blah blah");
LOG.info("This is logged");
LOG.debug("This is a debug and not logged");
LOG.error("This is an error and logged");
If you only want to log some text, then change the value for AcceptOnMatch to true and add DenyAllFilter.
Wednesday, May 23, 2012
mac: no acceptable C compiler found in $PATH
If you get the following error on mac:
1. download Xcode from https://developer.apple.com/downloads/index.action
2. install Xcode
3. Open Xcode, go to Preferences --> Downloads --> install "Command Line Tools"
4. relaunch terminal
configure: error: no acceptable C compiler found in $PATHHere is the step to fix it:
1. download Xcode from https://developer.apple.com/downloads/index.action
2. install Xcode
3. Open Xcode, go to Preferences --> Downloads --> install "Command Line Tools"
4. relaunch terminal
Saturday, May 19, 2012
Enable Spring Jdbc Transaction with Annotation
Spring 3.1 introduced a new annotation @EnableTransactionManagement. With it, all xml configuration can be got rid of now. Here is the example on how to use it:
https://github.com/zhentao/spring-jdbc-transaction-example
Next you need to annote your dao with @Transactional. The source code can be found at github:
@Configuration
@EnableTransactionManagement
public class JdbcConfig {
@Bean
public DataSource dataSource() {
//config datasource
}
@Bean
public PlatformTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public MyDao myDao() {
MyDaoJdbc dao = new MyDaoJdbc();
dao.setDataSource(dataSource());
return dao;
}
}
https://github.com/zhentao/spring-jdbc-transaction-example
Labels:
config,
EnableTransactionManagement,
jdbc,
Spring,
transaction
cobertura-maven-plugin
Recently I created a parent pom for my team to use. All my team's projects need to inherit this parent pom. This parent pom is rather simple:
<exclude>com/zhentao/Some.class</exclude> excludes Some.class from the cobertura report.
However, this flexibility can be abused by some developers. I got an build failed notification today:
<properties>We are targeting a pretty high unit test coverage, 90% for both line and branch coverage. However, some classes don't need unit test or don't need high coverage. Then each individual project can include the following to its project pom:
<default.encoding>UTF-8</default.encoding>
<default.jdk>1.6</default.jdk>
<line.coverage.target>90</line.coverage.target>
<branch.coverage.target>90</branch.coverage.target>
</properties>
<build>
<defaultGoal>install</defaultGoal>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>${default.jdk}</source>
<target>${default.jdk}</target>
<encoding>${default.encoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<configuration>
<encoding>${default.encoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version>
<configuration>
<encoding>${default.encoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.2.2</version>
</plugin>
<!-- Sonar uses these versions, below -->
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.7.1</version>
<configuration>
<targetJdk>${default.jdk}</targetJdk>
<encoding>${default.encoding}</encoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<encoding>${default.encoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.5.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<configuration>
<check>
<!-- Per-class thresholds -->
<branchRate>${branch.coverage.target}</branchRate>
<lineRate>${line.coverage.target}</lineRate>
<!-- Project-wide thresholds -->
<totalLineRate>${line.coverage.target}</totalLineRate>
<totalBranchRate>${branch.coverage.target}</totalBranchRate>
</check>
<formats>
<format>html</format>
</formats>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>clean</goal>
<goal>cobertura</goal>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<!-- Ensure that source code is packaged and deployed for inclusion into IDEs -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<build><ignore>org.slf4j.*</ignore> ignores all log statement in the code since we don't need to test logs.
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<configuration>
<instrumentation>
<ignores>
<ignore>org.slf4j.*</ignore>
</ignores>
<excludes>
<exclude>com/zhentao/Some.class</exclude>
<exclude>com/zhentao/other.class</exclude>
</excludes>
</instrumentation>
</configuration>
</plugin>
</plugins>
</build>
<exclude>com/zhentao/Some.class</exclude> excludes Some.class from the cobertura report.
However, this flexibility can be abused by some developers. I got an build failed notification today:
One developer excluded all classes from testing. What a developer!11:52:52 Archiving artifacts 11:52:52 [htmlpublisher] Archiving HTML reports... 11:52:52 [htmlpublisher] Archiving at PROJECT level /target/site/cobertura to /htmlreports/Cobertura_Report 11:52:52 ERROR: Specified HTML directory '/target/site/cobertura' does not exist. 11:52:52 Build step 'Publish HTML reports' changed build result to FAILURE
Friday, May 4, 2012
Yahoo hadoop tutorials
This is the link from yahoo for hadoop tutorials:
http://developer.yahoo.com/hadoop/tutorial/index.html
http://developer.yahoo.com/hadoop/tutorial/index.html
Subscribe to:
Posts (Atom)