Total Pageviews

2019/02/12

[Spring] java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

Problem
When I try to call web service via WebServiceTemplate, which provided by Spring framework. It throw the following exception:
java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty; nested exception is javax.net.ssl.SSLException: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
 at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:561)
 at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
 at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:378)

The code snippet is as following:
package demo.service.client;

import javax.net.ssl.TrustManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.ws.client.core.WebServiceTemplate;

import demo.dto.status;
import demo.dto.mapper.StatusMapper;
import demo.ws.bind.status.SendStatusRequest;
import demo.ws.bind.status.SendStatusResponse;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class StatusClient {
    
    @Autowired
    private WebServiceTemplate wsTemplate;
    
    @Value("${webservices.host}")
    private String host;
    
    @Value("${webservices.url.status}")
    private String url;
    
    @Autowired
    private StatusMapper mapper;

    public SendStatusResponse sendData(Status entity) {
        SendStatusRequest request = new SendStatusRequest();
        request.setStatusDto(mapper.toDto(entity));
        
        return (SendStatusResponse) wsTemplate.marshalSendAndReceive(host + url, request);
    }
    
}



How-To
Owing to the web service url is https, so you need to disable SSL certificate checking.

You need create an UnTrustworthyTrustManager:
package demo.service.client;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

public class UnTrustworthyTrustManager implements X509TrustManager {

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        // TODO Auto-generated method stub
        return null;
    }
}


And set UnTrustworthyTrustManager into MessageSender
package demo.service.client;

import javax.net.ssl.TrustManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.http.HttpsUrlConnectionMessageSender;

import demo.dto.status;
import demo.dto.mapper.StatusMapper;
import demo.ws.bind.status.SendStatusRequest;
import demo.ws.bind.status.SendStatusResponse;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class StatusClient {
    
    @Autowired
    private WebServiceTemplate wsTemplate;
    
    @Value("${webservices.host}")
    private String host;
    
    @Value("${webservices.url.status}")
    private String url;
    
    @Autowired
    private StatusMapper mapper;

    public SendStatusResponse sendData(Status entity) {
        HttpsUrlConnectionMessageSender sender = new HttpsUrlConnectionMessageSender();
        sender.setTrustManagers(new TrustManager[] { new UnTrustworthyTrustManager() });
        wsTemplate.setMessageSender(sender);
        
        SendStatusRequest request = new SendStatusRequest();
        request.setStatusDto(mapper.toDto(entity));
        
        return (SendStatusResponse) wsTemplate.marshalSendAndReceive(host + url, request);
    }
    
}


2019/02/11

[Java] BEA-380001-Internal Server Error

Scenario



How-To
The root cause is unknown. But comparing with workable client code with non-workable client code, there has one difference in package-info.java.

The package-info.java in workable client code is:
@javax.xml.bind.annotation.XmlSchema(namespace = "http://test.com", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package demo.ws.bind.test1;


The package-info.java in non-workable client code is:
package demo.ws.bind.test2;

Therefore, as I add annotation above package, the problem had been resolved.

2019/02/10

[Tool] [SoapUI] How to setup SOAP Web Service HTTP Basic Authentication

Scenario


How-To
Here has two steps to set up HTTP Basic Authentication : 
Step 1. Invoke context menu, and choose "Show Interface Viewer"


Step 2. Fill in user name and password.


2019/02/09

[SoapUI] A class/interface with the same name "xxxx" is already in use. Use a class customization to resolve this conflict.

Problem
When I try to generate SOAP web service client code via SoapUI, I get naming conflict exception:
[ERROR] A class/interface with the same name "xxxx" is already in use. Use a class customization to resolve this conflict. 



How-To
We can add customized argument in SoapUI to automatically resolve naming conflicts without requiring the use of binding customizations.



Reference

[1] http://cxf.apache.org/docs/wsdl-to-java.html

2019/02/08

[Spring] Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException

Problem
I have a  configuration which provide more than one implementations for a bean:
package demo.config;

import org.apache.http.auth.UsernamePasswordCredentials;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.http.HttpComponentsMessageSender;

@Configuration
public class WebServicesClientConfig {

    @Value("${webservice.username}")
    private String userName;

    @Value("${webservice.password}")
    private String password;

    @Bean (name = "webServiceTemplate")
    public Jaxb2Marshaller jaxb2Marshaller() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setPackagesToScan("demo.ws.bind");
        return jaxb2Marshaller;
    }

    @Bean
    public WebServiceTemplate webServiceTemplate() {
        WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
        webServiceTemplate.setMarshaller(jaxb2Marshaller());
        webServiceTemplate.setUnmarshaller(jaxb2Marshaller());

        webServiceTemplate.setMessageSender(httpComponentsMessageSender());

        return webServiceTemplate;
    }
    
    @Bean (name = "oq2WebServiceTemplate")
    public Jaxb2Marshaller jaxb2Marshaller2() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setPackagesToScan("demo.ws.bind2");
        return jaxb2Marshaller;
    }

    @Bean
    public WebServiceTemplate oq2WebServiceTemplate() {
        WebServiceTemplate oq2WebServiceTemplate = new WebServiceTemplate();
        oq2WebServiceTemplate.setMarshaller(jaxb2Marshaller2());
        oq2WebServiceTemplate.setUnmarshaller(jaxb2Marshaller2());

        oq2WebServiceTemplate.setMessageSender(httpComponentsMessageSender());

        return oq2WebServiceTemplate;
    }

    @Bean
    public HttpComponentsMessageSender httpComponentsMessageSender() {
        HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
        httpComponentsMessageSender.setCredentials(usernamePasswordCredentials());
        return httpComponentsMessageSender;
    }

    @Bean
    public UsernamePasswordCredentials usernamePasswordCredentials() {
        return new UsernamePasswordCredentials(userName, password);
    }

}


The client bean looks like:

package demo.client;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.ws.client.core.WebServiceTemplate;

@Component
public class OiClient {

    @Autowired
    @Qualifier("webServiceTemplate")
    private WebServiceTemplate webServiceTemplate;
    
    public Response getInfoByIdno() {
        // ignore details
    }

}

But as I start up spring boot, it throws the following exception:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'oiService': Unsatisfied dependency expressed through field 'oiClient'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'oiClient': Unsatisfied dependency expressed through field 'webServiceTemplate'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.ws.client.core.WebServiceTemplate' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=webServiceTemplate)}
 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:586) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:372) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1341) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062) ~[spring-beans-5.0.8.RELEASE.jar:5.0.8.RELEASE]


How-To
The configuration class should be fixed as bellows:
package demo.config;

import org.apache.http.auth.UsernamePasswordCredentials;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.http.HttpComponentsMessageSender;

@Configuration
public class WebServicesClientConfig {

    @Value("${webservice.username}")
    private String userName;

    @Value("${webservice.password}")
    private String password;

    @Bean
    @Qualifier("webServiceTemplate")
    public Jaxb2Marshaller jaxb2Marshaller() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setPackagesToScan("demo.ws.bind");
        return jaxb2Marshaller;
    }

    @Bean
    public WebServiceTemplate webServiceTemplate() {
        WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
        webServiceTemplate.setMarshaller(jaxb2Marshaller());
        webServiceTemplate.setUnmarshaller(jaxb2Marshaller());

        webServiceTemplate.setMessageSender(httpComponentsMessageSender());

        return webServiceTemplate;
    }
    
    @Bean
    @Qualifier("oq2WebServiceTemplate")
    public Jaxb2Marshaller jaxb2Marshaller2() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setPackagesToScan("demo.ws.bind2");
        return jaxb2Marshaller;
    }

    @Bean
    public WebServiceTemplate oq2WebServiceTemplate() {
        WebServiceTemplate oq2WebServiceTemplate = new WebServiceTemplate();
        oq2WebServiceTemplate.setMarshaller(jaxb2Marshaller2());
        oq2WebServiceTemplate.setUnmarshaller(jaxb2Marshaller2());

        oq2WebServiceTemplate.setMessageSender(httpComponentsMessageSender());

        return oq2WebServiceTemplate;
    }

    @Bean
    public HttpComponentsMessageSender httpComponentsMessageSender() {
        HttpComponentsMessageSender httpComponentsMessageSender = new HttpComponentsMessageSender();
        httpComponentsMessageSender.setCredentials(usernamePasswordCredentials());
        return httpComponentsMessageSender;
    }

    @Bean
    public UsernamePasswordCredentials usernamePasswordCredentials() {
        return new UsernamePasswordCredentials(userName, password);
    }

}


Reference
[1] https://www.logicbig.com/tutorials/spring-framework/spring-core/inject-bean-by-name.html

2019/02/07

[Java] How to do IP validation in Java?

Problem
How to do IP validation in Java?

How-To
Here has sample code:
    import lombok.Data;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Pattern;

    @Data
    public class Param {
        
        @NotNull(message = "請提供性別")
        private String gender;
        
        @NotNull(message = "請提供呼叫者 IP address")
        @Pattern(regexp = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", 
                 message = "呼叫者 IP address 不合法")
        private String srcIp;
    }

Reference
[1] https://stackoverflow.com/a/14877281/6314840

2019/02/06

[Spring] How to use shedlock in spring framework?

Problem
Owing to my web application will deploy to a cluster, so I need to find a solution to prevent concurrent execution of scheduled Spring tasks.


Here has a short example to demo how to use shedlock.


How-To
Add dependencies in pom.xml
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-spring</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>net.javacrumbs.shedlock</groupId>
            <artifactId>shedlock-provider-jdbc-template</artifactId>
            <version>1.0.0</version>
        </dependency>


Create table for shedlock
CREATE TABLE MY_BATCH_LOCK(
    name VARCHAR(64),
    lock_until TIMESTAMP(3) NULL,
    locked_at TIMESTAMP(3) NULL,
    locked_by  VARCHAR(255),
    PRIMARY KEY (name)
)



Add configuration in your spring boot application
package demo.config;

import java.time.Duration;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.ScheduledLockConfiguration;
import net.javacrumbs.shedlock.spring.ScheduledLockConfigurationBuilder;

@Configuration
public class ShedlockConfig {
    
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource, "MY_BATCH_LOCK");
    }

    @Bean
    public ScheduledLockConfiguration scheduledLockConfiguration(LockProvider lockProvider) {
        return ScheduledLockConfigurationBuilder
                .withLockProvider(lockProvider)
                .withPoolSize(10)
                .withDefaultLockAtMostFor(Duration.ofMinutes(10))
                .build();
    }
    
}


Usage:
    private static final int TEN_MIN = 10 * 60 * 1000;

    @Scheduled(cron = "0 0 1 * * ?")
    @SchedulerLock(name = "mySchedulerTask", lockAtMostFor = TEN_MIN, lockAtLeastFor = TEN_MIN)
    public void execute() {
    }