0
votes

I try to write a Java Jersey client for Akeneo PIM.

When in try to get acces token a have a 422 response status with :

{"code":422,"message":"Parameter \"grant_type\", \"username\" or \"password\" is missing, empty or invalid"}

I think there is a problem with the marshalization of the object in the Body.

When I remove the ClientConfig I have another problem :

com.sun.jersey.api.client.ClientHandlerException: com.sun.jersey.api.client.ClientHandlerException: A message body writer for Java type, class com.omb.akeneo.akeneoclient.json.UserAuthenticationInfoJson, and MIME media type, application/json, was not found

pom.xml :

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.omb.akeneo</groupId>
    <artifactId>akeneo-client</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>akeneo-client</name>
    <description></description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Jersy dependencies -->
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>1.19.4</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>1.19.4</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-bundle</artifactId>
            <version>1.19.4</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.10.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.10.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider -->
        **<dependency>
            <groupId>com.fasterxml.jackson.jaxrs</groupId>
            <artifactId>jackson-jaxrs-json-provider</artifactId>
            <version>2.10.1</version>
        </dependency>**


        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20190722</version>
        </dependency>


        <!-- Apache Commons lib -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.13</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <classifier>exec</classifier>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Object for the Post body

package com.omb.akeneo.akeneoclient.json;

import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty;

public class UserAuthenticationInfoJson {

private String username;
private String password;
private String grantType;

public UserAuthenticationInfoJson() {
}

@JsonCreator
public UserAuthenticationInfoJson(
        @JsonProperty("username") String username,
        @JsonProperty("password")String password,
        @JsonProperty("grant_type") String grantType) {
    super();
    this.username = username;
    this.password = password;
    this.grantType = grantType;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public String getGrantType() {
    return grantType;
}

public void setGrantType(String grantType) {
    this.grantType = grantType;
}

}

Object for Response

package com.omb.akeneo.akeneoclient.json;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.*;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "access_token",
        "expires_in",
        "token_type",
        "scope",
        "refresh_token"
})
public class UserTokenJson {

    @JsonProperty("access_token")
    private String accessToken;
    @JsonProperty("expires_in")
    private Integer expiresIn;
    @JsonProperty("token_type")
    private String tokenType;
    @JsonProperty("scope")
    private Object scope;
    @JsonProperty("refresh_token")
    private String refreshToken;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    @JsonProperty("access_token")
    public String getAccessToken() {
        return accessToken;
    }

    @JsonProperty("access_token")
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    @JsonProperty("expires_in")
    public Integer getExpiresIn() {
        return expiresIn;
    }

    @JsonProperty("expires_in")
    public void setExpiresIn(Integer expiresIn) {
        this.expiresIn = expiresIn;
    }

    @JsonProperty("token_type")
    public String getTokenType() {
        return tokenType;
    }

    @JsonProperty("token_type")
    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
    }

    @JsonProperty("scope")
    public Object getScope() {
        return scope;
    }

    @JsonProperty("scope")
    public void setScope(Object scope) {
        this.scope = scope;
    }

    @JsonProperty("refresh_token")
    public String getRefreshToken() {
        return refreshToken;
    }

    @JsonProperty("refresh_token")
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

}

My Client :

    package com.omb.akeneo.akeneoclient.client;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.LoggingFilter;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

import java.security.InvalidParameterException;

/**
 * Jersy client to acces Akeneo web api
 */
public class MyAkeneoClient {

    /**
     * credentials to acces at Akeneo api
     */
    private Credentials credential;

    /**
     * Endpoint of Akeneo api
     */
    private String endpoint;

    /**
     * Jersy client
     */
    private Client client;

    /**
     * Initialise a new Akeneo client
     * @param username
     * @param password
     * @param endpoint
     */
    public KAkeneoClient(String clientId, String secret, String username, String password, String endpoint) {

        // Check input params
        checkParams(clientId, secret, username, password, endpoint);

        this.credential = new Credentials(clientId, secret, username, password);
        this.endpoint = endpoint;

        **ClientConfig clientConfig = new DefaultClientConfig();
        clientConfig.getClasses().add(JacksonJsonProvider.class);
        this.client = Client.create(clientConfig);**

        this.client.addFilter(new LoggingFilter(System.out));

    }

    private void checkParams(String clientId, String secret, String username, String password, String endpoint) throws InvalidParameterException {
        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(password)) {
            throw new InvalidParameterException("The password parameter is not valid");
        }

        if(StringUtils.isBlank(endpoint)) {
            throw new InvalidParameterException("The endpoint parameter is not valid");
        }
    }

    public Client getClient() {
        return client;
    }

    public String getEndpoint() {
        return endpoint;
    }
}

Jersey client :

package com.omb.akeneo.akeneoclient.utils;

import com.omb.akeneo.akeneoclient.Constants;
import com.omb.akeneo.akeneoclient.client.MyAkeneoClient ;
import com.omb.akeneo.akeneoclient.json.UserAuthenticationInfoJson;
import com.omb.akeneo.akeneoclient.json.UserTokenJson;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.apache.commons.codec.binary.Base64;

import javax.ws.rs.core.MediaType;

public class AuthUtil {

    /**
     *
     * @param clientId
     * @param secret
     * @param endpoint
     * @param authenticationInfoJson
     * @return
     */
    public static String getAccesToken(final String clientId, final String secret, final String endpoint, final UserAuthenticationInfoJson authenticationInfoJson) {

        MyAkeneoClient  akeneoClient = new MyAkeneoClient (clientId, secret, authenticationInfoJson.getUsername(), authenticationInfoJson.getPassword(), endpoint);

        StringBuilder url = new StringBuilder(akeneoClient.getEndpoint()).append(Constants.GET_ACCES_TOKEN_ENDPOINT);
        WebResource webResource = akeneoClient.getClient().resource(url.toString());

        StringBuilder autorization = new StringBuilder("Basic ").append(AuthUtil.getBase64(clientId, secret));
        ClientResponse response = webResource
                .header("Authorization", autorization.toString())
                .type(MediaType.APPLICATION_JSON)
                .post(ClientResponse.class, authenticationInfoJson);
        if (response.getStatus() != 200) {
            throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
        }

        UserTokenJson userTokenJson = response.getEntity(UserTokenJson.class);

        return userTokenJson.getAccessToken();
    }

    /**
     *
     * get Base64 encode of clientId et secret
     * @param clientId
     * @param secret
     * @return
     */
    public static String getBase64(String clientId, String secret) {
        StringBuilder key = new StringBuilder(clientId).append(":").append(secret);
        String rs = clientId + ":" + secret;
        return Base64.encodeBase64String(rs.getBytes());
    }
}
1
1) You will need either a default constructor in the UserAuthenticationInfoJson class or you need to use @JsonCreator. 2) You are using Jackson 2 annotations, but the POJO mapping feature of jersey-son uses Jackson 1. If you want to use Jackson 2, then you want the dependency jackson-jaxrs-json-providers and register the JacksonJsonProvider with the client. If you don't do this, then you will need to use the old Jackson annotations, i.e. org.codehaus - Paul Samsotha
I made changes but i getting a http 422 error. You can see above changes - Ousmane MINTE

1 Answers

0
votes

I solved my problem, Apache doen't pass the authorization headers to PHP by default.

You should add this line : SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 in your VirtualHost definition :

/etc/apache2/sites-available/your_application.conf

<VirtualHost>
    .... other config....
    SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
    .... other config
</VirtualHost>

For people whos interest, I changed my implementation by using the Jersey spring-boot-starter and the json spring-boot-starter.

So now I have

pom

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-json</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Apache Commons lib -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.9</version>
    </dependency>
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.13</version>
    </dependency>

</dependencies>

Client

package com.omb.akeneo.akeneoclient.client;

import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.client.ClientConfig;
import org.springframework.web.filter.AbstractRequestLoggingFilter;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import java.security.InvalidParameterException;

/**
 * Jersy client to acces Akeneo web api
 */
public class MyAkeneoClient {

    /**
     * credentials to acces at Akeneo api
     */
    private Credentials credential;

    /**
     * Endpoint of Akeneo api
     */
    private String endpoint;

    /**
     * Jersy client
     */
    private Client client;

    /**
     * Initialise a new Akeneo client for omb
     * @param username
     * @param password
     * @param endpoint
     */
    public KAkeneoClient(String clientId, String secret, String username, String password, String endpoint) {

        // Check input params
        checkParams(clientId, secret, username, password, endpoint);

        this.credential = new Credentials(clientId, secret, username, password);
        this.endpoint = endpoint;

        client = ClientBuilder.newClient( new ClientConfig().register( AbstractRequestLoggingFilter.class ) );

    }

    private void checkParams(String clientId, String secret, String username, String password, String endpoint) throws InvalidParameterException {
        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(username)) {
            throw new InvalidParameterException("The username parameter is not valid");
        }

        if(StringUtils.isBlank(password)) {
            throw new InvalidParameterException("The password parameter is not valid");
        }

        if(StringUtils.isBlank(endpoint)) {
            throw new InvalidParameterException("The endpoint parameter is not valid");
        }
    }

    public Client getClient() {
        return client;
    }

    public String getEndpoint() {
        return endpoint;
    }
}

Jersey client

package com.omb.akeneo.akeneoclient.utils;

import com.omb.akeneo.akeneoclient.Constants;
import com.omb.akeneo.akeneoclient.client.MyAkeneoClient;
import com.omb.akeneo.akeneoclient.json.UserAuthenticationInfoJson;
import com.omb.akeneo.akeneoclient.json.UserTokenJson;
import org.apache.commons.codec.binary.Base64;

import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

public class AuthUtil {

    /**
     *
     * @param clientId
     * @param secret
     * @param endpoint
     * @param authenticationInfoJson
     * @return
     */
    public static String getAccesToken(final String clientId, final String secret, final String endpoint, final UserAuthenticationInfoJson authenticationInfoJson) {

        MyAkeneoClient akeneoClient = new MyAkeneoClient(clientId, secret, authenticationInfoJson.getUsername(), authenticationInfoJson.getPassword(), endpoint);

        StringBuilder url = new StringBuilder(akeneoClient.getEndpoint()).append(Constants.GET_ACCES_TOKEN_ENDPOINT);
        WebTarget webTarget = akeneoClient.getClient().target(url.toString());

        StringBuilder autorization = new StringBuilder("Basic ").append(AuthUtil.getBase64(clientId, secret));
        Invocation.Builder invocationBuilder =  webTarget.request(MediaType.APPLICATION_JSON);
        invocationBuilder.header("Authorization", autorization.toString());

        Response response = invocationBuilder.post(Entity.entity(authenticationInfoJson, MediaType.APPLICATION_JSON));
        if (response.getStatus() != 200) {
            throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
        }

        UserTokenJson userTokenJson = response.readEntity(UserTokenJson.class);

        return userTokenJson.getAccessToken();
    }

    /**
     *
     * get Base64 encode of clientId et secret
     * @param clientId
     * @param secret
     * @return
     */
    public static String getBase64(String clientId, String secret) {
        StringBuilder key = new StringBuilder(clientId).append(":").append(secret);
        String rs = clientId + ":" + secret;
        return Base64.encodeBase64String(rs.getBytes());
    }
}