Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

In the original blog entry, Throwing exceptions with messages from a SpringMVC controller I set up a system to allow exceptions that would be thrown to cause an HTTP status code to be sent to the consumer with a message.

I needed to expand upon that to handle all of the exceptions that my Spring MVC controllers could encounter. To accomplish that, I created a class and annotated it with @ControllerAdvice. This allowed me to create a ExceptionHandlers for all of my controllers in one place.

I created three mappings because I wanted to handle the three types of exceptions a little differently than the others.

  • ResourceException – allows me to set a HTTP status and a message
  • RuntimeException – these are not typically created by developers I need to grab all of the information that I can. That is why I used the toString. That contains the name of the exception and the system specified message.
  • Exception – these are checked exceptions and are created by developers within their code. The expectation is that they put a meaningful message in the message property.

This was fairly easy to accomplish by having three different exception mappings. Within each exception mapping, I set the values in the ResponseEntity to display the appropriate HTTP status with a message in the content.

package com.javaninja.cam.spring;


import com.javaninja.cam.exception.ResourceException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;


/**
 * Exception handler advice class for all SpringMVC controllers.
 * @author norrisshelton
 * @see ControllerAdvice
 */
@ControllerAdvice
public class CamControllerAdvice {

    /**
     * Returns whatever HTTP status was set within the ResourceException.  Uses the message that was set when
     * the ResourceException was created.
     * @param e SpringMVC controller exception.
     * @return http response entity
     * @see ExceptionHandler
     */
    @ExceptionHandler(ResourceException.class)
    public ResponseEntity handleResourceException(ResourceException e) {
        return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
    }

    /**
     * Sets the HTTP status as INTERNAL_SERVER_ERROR (500).  Uses the toString of the exception because
     * RuntimeExceptions don't always have enough usable information in the message property.
     * @param e SpringMVC controller exception.
     * @return http response entity
     * @see ExceptionHandler
     */
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity handleRuntimeException(RuntimeException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.toString());
    }

    /**
     * Sets the HTTP status as INTERNAL_SERVER_ERROR (500).  Uses the message property under the assumption that the
     * developer that created the exception set a meaningful explanation in the message property.
     * @param e SpringMVC controller exception.
     * @return http response entity
     * @see ExceptionHandler
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity handleException(Exception e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
    }
}

November 16th, 2016

Posted In: Java, java ninja, Javaninja, Spring, Spring MVC

One Comment

The Maven Checkstyle Plugin is very useful to identity source files that do not follow your source coding standards. We chose to follow what Google used for their Checkstyle as the basis for ours. We made small tweaks to their standard, but those are contained within the declared file. Please note that Google used a new rule that wasn’t contained within the libraries that were included by the version of the maven-checkstyle-plugin that was available. To enable this additional functionality, we explicitly declared the version of the puppycrawl checkstyle dependency to use.

I configured the plugin to generate reports and to not fail on error, but to give warnings instead.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-checkstyle-plugin</artifactId>
    <version>2.17</version>
    <dependencies>
        <!--
        http://mvnrepository.com/artifact/com.puppycrawl.tools/checkstyle
        Needed because Google uses a new checkstyle that isn't included by the regular plugin.
        https://groups.google.com/forum/#!topic/checkstyle/OXux8RqJuXg
        -->
        <dependency>
            <groupId>com.puppycrawl.tools</groupId>
            <artifactId>checkstyle</artifactId>
            <version>6.19</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <goals>
                <goal>checkstyle</goal>
            </goals>
            <phase>verify</phase>
        </execution>
    </executions>
    <configuration>
        <configLocation>twinspires_checkstyle.xml</configLocation>
        <encoding>UTF-8</encoding>
        <consoleOutput>true</consoleOutput>
        <failOnViolation>false</failOnViolation>
        <linkXRef>false</linkXRef>
        <enableRulesSummary>true</enableRulesSummary>
        <enableSeveritySummary>true</enableSeveritySummary>
        <enableFilesSummary>true</enableFilesSummary>
        <includeTestSourceDirectory>true</includeTestSourceDirectory>
    </configuration>
</plugin>
<plugin>

The output of the plugin will contain the name of the Java class, the line number and a brief description of what the warning is, along with the checkstyle caregory. Example output is below:

[INFO] --- maven-checkstyle-plugin:2.17:checkstyle (default) @ newcam-server ---
[INFO] Starting audit...
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:10: Using the '.*' form of import should be avoided - com.twinspires.cam.entity.*. [AvoidStarImport]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:58: Using the '.*' form of import should be avoided - com.twinspires.cam.response.*. [AvoidStarImport]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:82: First sentence of Javadoc is incomplete (period is missing) or not present. [SummaryJavadoc]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:98: Abbreviation in name 'registerWageringCustomerInRAFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:125: Abbreviation in name 'updateCustomerSSNFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:177: First sentence of Javadoc is incomplete (period is missing) or not present. [SummaryJavadoc]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:194: First sentence of Javadoc is incomplete (period is missing) or not present. [SummaryJavadoc]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:350:5: Missing a Javadoc comment. [JavadocMethod]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:363:5: Missing a Javadoc comment. [JavadocMethod]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:517:9: '}' at column 9 should be on the same line as the next part of a multi-block statement (one that directly contains multiple blocks: if/else-if/else or try/catch/finally). [RightCurly]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:533:20: Method name 'eCommerceCustomerPreUpdateCheck' must match pattern '^[a-z][a-z0-9][a-zA-Z0-9_]*$'. [MethodName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:538: Abbreviation in name 'emailDTO' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:554: Abbreviation in name 'dtoCountryByISOCode' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:755:5: Missing a Javadoc comment. [JavadocMethod]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:801: Distance between variable 'failReason' declaration and its first usage is 7, but allowed 3.  Consider to make that variable as final if you still need to store its value in advance (before method calls that might do side effect on original value). [VariableDeclarationUsageDistance]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:940: Abbreviation in name 'setRegisterWageringCustomerInRAFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:941: Abbreviation in name 'registerWageringCustomerInRAFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:1155: Abbreviation in name 'setUpdateCustomerSSNFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:1155: Abbreviation in name 'updateCustomerSSNFacade' must contain no more than '1' capital letters. [AbbreviationAsWordInName]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/CustomerServicesManagerImpl.java:1257: Line is longer than 120 characters (found 129). [LineLength]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/ICustomerServicesManager.java:47: Using the '.*' form of import should be avoided - com.twinspires.cam.response.*. [AvoidStarImport]
[WARN] /Users/norrisshelton/IdeaProjects/newcam/newcam-server/src/main/java/com/twinspires/cam/biz/ICustomerServicesManager.java:52: Abbreviation in name 'verifySSN' must contain no more than '1' capital letters. [AbbreviationAsWordInName]

October 19th, 2016

Posted In: java ninja, Javaninja, Maven

Leave a Comment

The Maven Dependency Plugin can give you dependency related information for your Maven projects. I use it for two main purposes

  • Dependencies that I declare, but do NOT use
  • Dependencies that I do NOT care, but do use

The minimal configuration is as follows:

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>analyze</id>
                        <goals>
                            <goal>analyze-only</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

The normal behavior is to break the build when there is a problem. I was working on a pre-existing project and we were going to have to work through the problems over time, so this was counter-productive. To disable this behavior, I set the plugin to to fail on warnings. Another thing that I do is enable the output of XML for any used, but undeclared dependencies. This makes it very easy to copy/paste the missing dependencies.

            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>analyze</id>
                        <goals>
                            <goal>analyze-only</goal>
                        </goals>
                        <configuration>
                            <failOnWarning>false</failOnWarning>
                            <outputXML>true</outputXML>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

This is what sample output will look like. There are three different sections.

  • Used, but undeclared dependencies. These should be added to your POM file. You can copy the XML elements in the last section to easily add these.
  • Unused, but declared dependencies. These can usually be removed from your POM files. Please be aware that some dependencies are not explicitly declared and are determined at run-time (e.g Spring has multiple implementations for various functions).
  • The XML for the used, but undeclared dependencies. These XML snippets should be added to your POM files. This prevents your project from breaking when a dependency library changes its dependencies and inadvertantly breaks your build.
[INFO] --- maven-dependency-plugin:2.10:analyze-only (analyze) @ newcam-server ---
[WARNING] Used undeclared dependencies found:
[WARNING]    com.google.code.gson:gson:jar:2.5:compile
[WARNING] Unused declared dependencies found:
[WARNING]    com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:2.7.0:compile
[WARNING]    org.hibernate:hibernate-validator:jar:4.1.0.Final:compile
[WARNING]    mysql:mysql-connector-java:jar:5.1.34:provided
[WARNING]    org.hibernate:hibernate-entitymanager:jar:4.3.11.Final:compile
[WARNING]    org.slf4j:jcl-over-slf4j:jar:1.7.13:compile
[WARNING]    org.springframework:spring-orm:jar:4.2.4.RELEASE:compile
[WARNING]    org.slf4j:log4j-over-slf4j:jar:1.7.13:compile
[WARNING]    org.apache.commons:commons-dbcp2:jar:2.1:test
[WARNING]    org.apache.cxf:cxf-rt-frontend-jaxrs:jar:3.1.4:compile
[WARNING]    ch.qos.logback:logback-classic:jar:1.1.3:compile
[WARNING]    org.codehaus.jackson:jackson-xc:jar:1.9.13:compile
[INFO] Add the following to your pom to correct the missing dependencies: 
[INFO] 
<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.5</version>
</dependency>
[INFO] 

October 19th, 2016

Posted In: java ninja, Javaninja, Maven

Leave a Comment

We had an interceptor that was written for Hibernate 3. When we went to Hibernate 4, we discovered that methods that we were used to using didn’t exist anymore. The original Hibernate 3 code looked like:

Session session = null;
MySQLMatchingRowCountInterceptor interceptor = new MySQLMatchingRowCountInterceptor();
session = ((Session) getROEntityManager().getDelegate()).getSessionFactory().openSession(interceptor);
session.beginTransaction();
interceptor.initialize(session);

Unfortunately, SessionFactory.openSession() no longer takes an interceptor.

To define an interceptor, you need to create the Session from the SessionFactory with the .withOptions() method. Once you do that, you can specify the interceptors.

Session session = null;
MySQLMatchingRowCountInterceptor interceptor = new MySQLMatchingRowCountInterceptor();
session = ((Session) getROEntityManager().getDelegate()).getSessionFactory()
                                                        .withOptions()
                                                        .interceptor(interceptor)
                                                        .openSession();
session.beginTransaction();
interceptor.initialize(session);

The Maven dependency we were using for Hibernate 4.3.11 is:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>${hibernate.version}</version>
</dependency>

October 11th, 2016

Posted In: hibernate, Java, java ninja, Javaninja

Leave a Comment

The Problem

We had a service written in Java that retrieved a number from the database and returned it through a RESTful service to a Javascript client. The number was a long because of it’s large size. This had worked for a very long time. However, we noticed that when we had some very large numbers, the number started to be off by one. Specifically,

9164083756384584
9597642508289680

Those values were displayed as

9164083756384585
9597642508289681

In Java, we are used to overflow behavior being represented by a number turning negative because the signed bit is being used to represent data.

The Findings

We were able to verify that when we called the service with Postman, that we did see the correct value. The problem occurred when the Javascript client tried to parse the data. There was a specific line where the value was taken from the json to string marshalled object and it was converted to a string. This is where the problem was. After much searching and consulting with our Javascript experts, we ended up with ECMA 8.5 The Number Type. What an eye opener. This confirmed to us that the largest number that we could expect to be retrieved from our Java RESTful services was 2^53-1, not 2^63-1.

We had no idea that 9007199254740991 was the largest value that we could expect to be represented accurately in Javascript.

The Solution

Our solution was to add a property to the object that we were returning for marshalling by our Java RESTful services. We updated the service to have an additional property that was a String. Meaning that we would transition to return a String as our record IDs, instead of a long. This was fine, since arithmetic operations shouldn’t be performed on our object IDs anyway.

More Findings

We also found supporting information at

  • http://stackoverflow.com/questions/307179/what-is-javascripts-highest-integer-value-that-a-number-can-go-to-without-losin
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_VALUE

October 4th, 2016

Posted In: ECMA, ECMAscript, Java, java ninja, Javaninja, javascript

Leave a Comment

The SpringFramework has a RestTemplate object that can be used to HTTP-based network calls. It doesn’t timeout by default, but there is functionality that can be configured. A RequestFactory must be created, then it needs to be wired into the RestTemplate.

The readTimeout and the connectionTimeout are both expressed in milliseconds.

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd>
 
    <bean id="requestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"
          p:readTimeout="250"
          p:connectTimeout="250"/>
 
    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"
          c:requestFactory-ref="requestFactory"/>
</beans>

Once you have the RequestFactory configured, then the RestTemplate will begin to throw SocketTimeoutException and ConnectTimeoutException, depending on the problem. This is normally handled by catching RestClientException. This RuntimeException wraps all exceptions that are throws by the RestTemplate.

String response = null;
try {
    response = restTemplate.getForObject(url, String.class);
} catch (RestClientException e) {
    createOrderResponse.setStatus("failure");
    logger.error("Unable to get the PayNearMe barcode URL", e);
    //FIXME do something with the exception
    //throw e;
}

Catching the RestClientException is usually enough, because you only want to know that there is a problem. However, if you need to know the specific problem, you can test the RootCause for the specific type of exception.

String response = null;
try {
    response = restTemplate.getForObject(url, String.class);
} catch (RestClientException e) {
    createOrderResponse.setStatus("failure");
    if (e.getRootCause() instanceof SocketTimeoutException) {
        logger.error("SocketTimeoutException", e);
    } else if (e.getRootCause() instanceof ConnectTimeoutException) {
        logger.error("ConnectTimeoutException", e);
    } else {
        logger.error("Some other exception", e);
    }
    //FIXME do something with the exception
    //throw e;
}

September 13th, 2016

Posted In: Java, java ninja, Javaninja, Spring

Leave a Comment

I don’t do a lot with Hibernate directly, but sometimes I do. I had seen Hibernate inheritance before, but I never had to make one. The pattern that was in place was there was a table that had common information. There was another table that had information that was implementation-specific and there were multiple implementations. My job was to add another implementation. To accomplish the writing of the data, they had a Hibernate entity for the common table. This entity was extended by an implementation-specific entity. Then they would populate the entity and save it. Effectively writing to two tables with one logical write.

The InheritanceType is JOINED. This sets it up to be joined on by other tables (e.g. extended). The base entity was defined as follows:

@Entity
@Table(name = "FundsAudit")
@Inheritance(strategy = InheritanceType.JOINED)
public class FundsAudit {

    @Id
    @Column(name = "FundTypeKey")
    private String fundTypeKey;

    //...

}

I had to add an @InheritanceType as SINGLE_TABLE. Which basically says a table per entity. I also specified @PrimaryKeyJoinColumn. The referencedColumnName is the name of the column in the inherited table. The name is the name of the column in the entity that is extending the base table. Note that you don’t see that column listed in the entity that is being extended. That table does in fact exist. This is what I came up with to extend the base entity.

@Entity
@Table(name = "paynearme_audit")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@PrimaryKeyJoinColumn(referencedColumnName = "FundTypeKey", name = "FundTypeKey")
public class PayNearMeAudit extends FundsAudit {

    //@Id  NOTE:  You don't use ID in an entity that is inherited

    @Column(name = "PayNearMe_Order_ID")
    private long payNearMeOrderIdentifier;

    //...

}

Once I have my entities defined, it is very simple to use the object. I use the entity that I created, that extends the base entity. In my code, that is all you see referenced. I have fields from both entities/tables available to me.

PayNearMeAudit payNearMeAudit = new PayNearMeAudit();
payNearMeAudit.setAffiliateId(affiliateId);
payNearMeAudit.setHandler(String.valueOf(accountNumber));
payNearMeAudit.setPayNearMeOrderIdentifier(Long.parseLong(payNearMeOrderIdentifier));
payNearMeAudit.setTransactionDateTime(new Date());
payNearMeAudit.setTransactionType(TransactionType.Deposit.toString());
payNearMeAudit.setStatus("initiated");
payNearMeAudit.setAmount(amount);
payNearMeAudit.setFundTypeKey(new IdGeneratorImpl().generateOrderId(FundingCache.get(Constants.SITE_ID)));
payNearMeAudit.setFundType(FundType.PayNearMe.toString());
payNearMeAudit.setResponse("{\"payNearMeOrderIdentifier\":\"" + payNearMeOrderIdentifier + "\"}");
payNearMeAuditDao.insert(payNearMeAudit);
payNearMeAudit = payNearMeAuditDao.findByPayNearMeOrderIdentifier(Long.parseLong(payNearMeOrderIdentifier));

I need to ensure that I specify the value of the join column, fundTypeKey. Other than that, it just works. If you are reading the code, you would never have any idea that you were doing anything out of the ordinary.

August 30th, 2016

Posted In: hibernate

Leave a Comment

I was working on Java some code. It had two different objects that extended a common base object. There was a method that was called to load the common values. The receiving methods would then cast the returned object to the type that it was given. Here was the original code:

private ConvergeBaseRequest setProperties(ConvergeBaseRequest request) {
    // do some stuff
    request.setSslMerchantId(merchantId);
    request.setSsluserId(userId);
    request.setSslPin(pin);
    return request;
}

It was called by:

request = (ConvergeCCSaleRequest) setProperties(request);

After a little tweaking, I came up with the following method:

private <T extends ConvergeBaseRequest> T setProperties(T request) {
    // do some stuff
    request.setSslMerchantId(merchantId);
    request.setSsluserId(userId);
    request.setSslPin(pin);
    return request;
}

This made it simpler to call also,

request = setProperties(request);

With just a little bit of smarts, we were able to use Java Generics to make our calling code a little bit simpler.

July 19th, 2016

Posted In: Java, java ninja, Javaninja

Leave a Comment

I like to use SpringMVC for my JSON controllers. I do not prefer to return HTTP status codes other than 200, but sometimes you don’t have a choice. I was working on a project where the service would return the requested resource when it is available. If the resource is not found, then it is supposed to return an HTTP status of 404 with a messages. I thought this would be included right out of the box, but it wasn’t.

What Spring MVC offers and why it didn’t work

With Spring MVC, you have the ability to define an exception and have that exception return a specified HTTP status code when it reaches the controllers. Here is what an exception class would look like:

package com.twinspires.brisdataapi.bdsserver.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @author norris.shelton
 */
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException  extends RuntimeException {
    /**
     * Constructs a new runtime exception with the specified detail message.
     * The cause is not initialized, and may subsequently be initialized by a
     * call to {@link #initCause}.
     * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()}
     *                method.
     */
    public NotFoundException(String message) {
        super(message);
    }
}

This does provide a way to specify a message, but it doesn’t get displayed when a message is returned by the controllers. Ugh.

This would provide a good start, but I would need an exception handler to get the message. Unfortunately, all of the examples of the exception handlers show a method per exception.

Doing the least to make it work

This details the changes that I needed to make to allow the service to function correctly. I wanted to create as few classes as possible.

Define an exception

I created an exception that extends RuntimeException. I didn’t add a @ResponseStatus because I didn’t want to have to make an exception for each status code. I created a constructor that takes an HTTP status code and a message. This allows me to have one exception that I can reuse in all of my controllers and always return the correct status and message.

package com.twinspires.brisdataapi.bdsserver.exceptions;

import org.springframework.http.HttpStatus;

/**
 * @author norris.shelton
 */
@SuppressWarnings("ClassWithoutNoArgConstructor")
public class BdsServiceException extends RuntimeException {

    private HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

    public HttpStatus getHttpStatus() {
        return httpStatus;
    }

    /**
     * Constructs a new runtime exception with the specified detail message.
     * The cause is not initialized, and may subsequently be initialized by a
     * call to {@link #initCause}.
     * @param message the detail message. The detail message is saved for later retrieval by the {@link #getMessage()}
     *                method.
     */
    public BdsServiceException(HttpStatus httpStatus, String message) {
        super(message);
        this.httpStatus = httpStatus;
    }
}

Define SpringMVC exception handler

Now that I have an exception that has all of the information that I want returned to the caller of my service, I need to define an exception handler. It is the same type of exception handler that you normally see, except it returns a ResponseEntity. With the ResponseEntity, I can return the retrieve the correct status code and message from the exception and return them to the caller.

package com.twinspires.brisdataapi.bdsserver.spring;

import com.twinspires.brisdataapi.bdsserver.exceptions.BdsServiceException;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ExceptionHandlerAdvice { 
 
    @ExceptionHandler(BdsServiceException.class)
    public ResponseEntity handleException(BdsServiceException e) {
        // log exception 
        return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
    }         
} 

Putting it to use

All that is left is throwing the exception. I threw the exception with the desired HTTP status code and the message.

            throw new ResourceException(HttpStatus.NOT_FOUND, "We were unable to find the specified resource.");

June 22nd, 2016

Posted In: Java, java ninja, Javaninja, Spring, Spring MVC

2 Comments

We were using FasterXML on the same object to generate JSON and XML. Unfortunately, the object itself didn’t define the desired markup. It used JAXB annotations to add a wrapper around a set of data.

XML and JSON output based upon class definition

The class had the following property defined:

public List<TestRacingEventResponse> getRacingEvents() { return _racingEvents; }
public void setRacingEvents(List<TestRacingEventResponse> racingEvents) { this._racingEvents = racingEvents; }

This would generate XML like:

<racingEvents>
    <....>
</racingEvents>
<racingEvents>
    <....>
</racingEvents>
<racingEvents>
    <....>
</racingEvents>

The JSON would be outputted like this:

{racingEvents
{…}
}
{racingEvents
{…}
}
{racingEvents
{…}
}

JAXB to create a wrapper and redefine the element

This isn’t what was wanted. The desired output was a racingEvents container around a list of racingEvent containers. This is quite easy in XML via JAXB. The declaration with the required JAXB is as follows:

@XmlElementWrapper(name="racingEvents")
@XmlElement(name="racingEvent")
public List<TestRacingEventResponse> getRacingEvents() { return _racingEvents; }
public void setRacingEvents(List<TestRacingEventResponse> racingEvents) { this._racingEvents = racingEvents; }

This would generate XML like:

<racingEvents>
    <racingEvent>
        <....>
    </racingEvent
    <racingEvent>
        <....>
    </racingEvent
    <racingEvent>
        <....>
    </racingEvent
</racingEvents>

This works great for XML, but JSON doesn’t honor JAXB annotations. This leaves us with two alternatives. Create a class structure that mimics the structures you wanted. That would be creating a RacingEvents property in our enclosing class that is a List, then create a RacingEvent object that has the required fields.

Configure FasterXML (Jackson 2) to honor JAXB Wrapper annotation

Another way to do this is to instruct the JSON marshaller to honor the JAXB annotations. We are using Jackson 2 (FasterXML). In our particular case, we were using CXF for the front-end services. The original XML configuration is as follows:

<jaxrs:server id="hptsWebService" address="/">
    <jaxrs:serviceBeans>
        <bean id="accountServices" class="com.twinspires.hpts.servicedefinitions.AccountServices"/>
        <bean id="racingContentServices" class="com.twinspires.hpts.servicedefinitions.RacingContentServices"/>
        <bean id="systemInfoServices" class="com.twinspires.hpts.servicedefinitions.SystemInfoServices"/>
        <bean id="testerServices" class="com.twinspires.hpts.servicedefinitions.TesterServices"/>
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>
    </jaxrs:providers>
</jaxrs:server>

I needed to be able to configure the ObjectMapper. Unfortunately, I couldn’t get to the ObjectMapper in the code because the JacksonJaxbJsonProvider only exposed a MAPPER. That seems like a bug to me. To get around this, I declared the JacksonJaxbJsonProvider separately.

<bean id="jacksonJaxbJsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>

<jaxrs:server id="hptsWebService" address="/">
    <jaxrs:serviceBeans>
        <bean id="accountServices" class="com.twinspires.hpts.servicedefinitions.AccountServices"/>
        <bean id="racingContentServices" class="com.twinspires.hpts.servicedefinitions.RacingContentServices"/>
        <bean id="systemInfoServices" class="com.twinspires.hpts.servicedefinitions.SystemInfoServices"/>
        <bean id="testerServices" class="com.twinspires.hpts.servicedefinitions.TesterServices"/>
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <ref bean="jacksonJaxbJsonProvider"/>
    </jaxrs:providers>
</jaxrs:server>

Now that the object is separate, I had to find a place where I could manipulate it. I found an object that was related to the functionality that I needed and added a constructor that took the JacksonJaxbJsonProvider as input. Once I had that object, I could configure to my heart’s content.

My modifications involved creating a new OjbectMapper. I preferred to get the ObjectMapper that was being used, but there was no getter. Once I have the ObjectMapper, I set a property that indicates that I want use the XmlElementWrapper as a wrapper for my output.

    @Autowired
    public HttpUtil(JacksonJaxbJsonProvider jacksonJaxbJsonProvider) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(MapperFeature.-- USE_WRAPPER_NAME_AS_PROPERTY_NAME);
        jacksonJaxbJsonProvider.setMapper(objectMapper);
    }

Now that we have told the JSON marshaller to honor the JAXB wrapper annotation, output should be:

{racingEvents
    {races
        {...}
    }
    {races
        {...}
    }
    {races
        {...}
    }
}

Conclusion

To have a FasterXML honor JAXB annotations when it outputs JSON, you have to instantiate the JacksonJaxbJsonProvider separately so it can be injected. Once it is injected in code, you can create and configure an ObjectMapper.

June 10th, 2016

Posted In: FasterXML, Jackson 2, Java, java ninja, Javaninja, JAXB, json

One Comment

« Previous PageNext Page »
LinkedIn Auto Publish Powered By : XYZScripts.com