ORVE WS (Dynamic) (2). First things first in programming.

1. Introduction


The following libraries (or tools) will be used:

  • Apache CXF (for web services)
  • Lombok  (for avoiding setters and getters boilerplate)
  • Maven
  • XML and WS libraries included in JVM 1.8 but not in 9 and upwards.
But I am a fan of Jackson so these libraries will be used:
  • jackson-dataformat-xml (For XML )

Apache has some gems for reflection and string management
  • apache-commons-lang3

2. Configuration with pom.xml

Here is the pom.xml file that aims to be compatible with Java 9 and upwards. In this example, Java 10 is used.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>EduWS</groupId>
  <artifactId>ORVEWS</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>ORVE WS</name>
  <description>ORVE WS Client</description>
  
  <properties>
    <cxf.version>3.2.6</cxf.version>
    <jax.version>2.3.0</jax.version>
    <lombok.version>1.18.2</lombok.version>
    <jackson.version>2.9.6</jackson.version>
        
    <failOnMissingWebXml>false</failOnMissingWebXml>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- Java version-->
    <java.version>10</java.version>
  </properties>  
    
  <dependencies>
  
    <!-- WS DEPENDENCIES -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <version>${cxf.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>${cxf.version}</version>
    </dependency>
        <!-- Jetty is needed if you're are not using the CXFServlet -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http-jetty</artifactId>
        <version>${cxf.version}</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>${lombok.version}</version>
      <scope>provided</scope>
    </dependency>
    <!-- END WS DEPENDENCIES -->
       
    
    <!--  Apache commons string utils .. -->
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.7</version>
    </dependency>
    
    <!--BEGIN Java 9 references to JEE  not included in JDK9-->
    <!-- see http://openjdk.java.net/jeps/320-->
    
    <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl -->
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>${jax.version}</version>
    </dependency>
        
    <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-core -->
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-core</artifactId>
      <version>${jax.version}</version>
    </dependency>
    
    
    <dependency>
      <groupId>com.sun.activation</groupId>
      <artifactId>javax.activation</artifactId>
      <version>1.2.0</version>
    </dependency>
    
    <dependency>
      <groupId>com.sun.xml.ws</groupId>
      <artifactId>jaxws-ri</artifactId>
      <version>${jax.version}</version>
      <type>pom</type>
    </dependency>
        
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-ri</artifactId>
      <version>${jax.version}</version>
      <type>pom</type>
    </dependency>
    
    <!-- End of Dependencies required for JAVA 9!!!! -->
    
       
    <!-- 2018-09-24 Jackson Dataformat XML -->
    <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    
    
      
  </dependencies>
  
</project>



3. The static way. Invoking "obtenerIdentificadores"

Here is a class that implements a static client:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package ximodante.orve.staticp;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.ws.Holder;

import https.ssweb_seap_minhap_es.demoorve.FiltrosIdentificadores;
import https.ssweb_seap_minhap_es.demoorve.ObtenerIdentificaoresRespuestaWS;
import https.ssweb_seap_minhap_es.demoorve.Security;
import https.ssweb_seap_minhap_es.demoorve.WSExportacionPortType;
import https.ssweb_seap_minhap_es.demoorve.WSExportacionService;

public class StaticORVEClient {
 private static final QName SERVICE_NAME = new QName("https://ssweb.seap.minhap.es/demoorve/", "WSExportacionService");

    private StaticORVEClient() {
    }

    public static void main(String args[]) throws java.lang.Exception {
        URL wsdlURL = WSExportacionService.WSDL_LOCATION;
        if (args.length > 0 && args[0] != null && !"".equals(args[0])) {
            File wsdlFile = new File(args[0]);
            try {
                if (wsdlFile.exists()) {
                    wsdlURL = wsdlFile.toURI().toURL();
                } else {
                    wsdlURL = new URL(args[0]);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }

        //1. WS Ports 
        WSExportacionService ss = new WSExportacionService(wsdlURL, SERVICE_NAME);
        WSExportacionPortType port = ss.getWSExportacionPort();

                
        //2. Obtener identificadores
        System.out.println("1. Invoking obtenerIdentificadores...");
        
        //2.1 Invoking param: Holder<Security>
        //2.1.1 Security
        Security security = new Security();
        security.setUsername("myUser");
        security.setPassword("myPassword");
        //2.1.2 Holder<Security> Invoking param: Security
        Holder<Security> hSec = new Holder<Security>(security);
        
        //2.2 Invoking param: FiltrosIdentificadores
        FiltrosIdentificadores filtIden = new FiltrosIdentificadores();
        filtIden.setEstado("EC");
        filtIden.setFechaInicio("2018-09-01 00:00:01");
        filtIden.setFechaFin("2018-09-23 23:59:59");
        filtIden.setUnidad("LA1000323");
        //filtIden.setOficina("O00002721");
        
        //2.3 Invoke operation obtenerIdentificadores
        ObtenerIdentificaoresRespuestaWS obtIdenResp = port.obtenerIdentificadores(hSec, filtIden);
        if (! obtIdenResp.getCodigo().equals("00")) { 
          System.out.println("Error " + obtIdenResp.getCodigo() + "->" + obtIdenResp.getDescripcion());
        } else {
          
         int ii=0;
         for (Integer iden: obtIdenResp.getIdentificadores().getItem()) {
          System.out.println(ii++ + "--> id="+ iden.intValue());
         } 
        } 
       
        System.exit(0);
    }

}

 If we want to create a dynamic client, let's first create this utilitiy class

4. Dynamic way (1) Utility class


In order to simplify creating instances and assigning the values to attributes, a utility class is implemented.

To create an instance of a specific class, it is required to supply the name of the class and a Map<String, Object> of the pairs attribute name - attribute value


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package ximodante.utils;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;

import org.apache.commons.lang3.reflect.FieldUtils;

public class ObjectBuilder {
 
 /**
  * Creates an object from its canonical class name 
  *   and assing values to attributes from a map containing
  *   attribute name and value
  * @param fullClassName
  * @param fields
  * @return
  * @throws ClassNotFoundException
  * @throws InstantiationException
  * @throws IllegalAccessException
  * @throws IllegalArgumentException
  * @throws InvocationTargetException
  * @throws NoSuchMethodException
  * @throws SecurityException
  */
 public static Object getObject(String fullClassName, Map<String,Object> fields ) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
  
  //1. Get a class loader
  ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
  
  //2, Load a class
  Class<?> MyClass=null;
  MyClass=classLoader.loadClass(fullClassName);
  
  //3. Get an instance of the class
  Object myObject = MyClass.getConstructor().newInstance();
  
  //4. Assign field values to object
  for (String s: fields.keySet()) {
   FieldUtils.writeField(myObject, s, fields.get(s) , true);
  }
  return myObject;
 }

 public static void main(String[] args) {
  // TODO Auto-generated method stub

 }

}

So, some boilerplate code is simplified.


4. Dynamic way (2).Invoking "obtenerIdentificadores"


This class creates a dynamic client and the 2 parameter classes to invoke the operation in the WS.

Here is the class


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package ximodante.orve.dynamicp;

import java.util.HashMap;
import java.util.List;

import javax.xml.ws.Holder;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

import ximodante.utils.ObjectBuilder;

public class DynamicORVEClient {

 public static void main(String[] args) { 
  
  try {
      
   //1. Declare WSDL
   JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
   Client client = dcf.createClient("https://ssweb.seap.minhap.es/demoorve/WSExportacion.wsdl");
   
            // 2. Create a map to store fields
   var mapFields=new HashMap<String,Object>();
  
   // 3. Create parameters of the WS operations
   
   //2.1 Security
   //2.1.1 Assign fields
   mapFields.clear(); // remove previous fields
   mapFields.put("username", "myUser");
   mapFields.put("password", "myPassword");
   
   //2.1.2 Create object
   var security=ObjectBuilder.getObject("https.ssweb_seap_minhap_es.demoorve.Security", mapFields);

   //2.2 Holder<Security>
   var secHolder = new Holder(security);
   
  
   //2.3 FiltrosIdentificadores
   //2.3.1 Assign fields
   mapFields.clear(); // remove previous fields
   
   mapFields.put("estado", "EC");
   mapFields.put("fechaInicio", "2018-09-01 00:00:01");
   mapFields.put("fechaFin", "2018-09-23 23:59:59");
   mapFields.put("unidad", "LA1000323"); //Catarroja
   //mapFields.put("oficina", "O00002721");// Of. Registro de Catarroja
   
   
   //2.3.2 Create object
   var filtros=ObjectBuilder.getObject("https.ssweb_seap_minhap_es.demoorve.FiltrosIdentificadores", mapFields);

  
   //3 Invoke WS operation
  
   // 3.1 Output object (obtained by invoking the WS operation)
   Object[] res=null;

   // 3.2 Operation name from the WSDL 
   String operationWS="obtenerIdentificadores"; 

   // 3.3 WS invocation with operation name and input structure
   //res = client.invokeWrapped(operationWS,secHolder,filtros);
   res = client.invoke(operationWS,secHolder,filtros);
   
   var b=res[1]; //Holder<obtenerIdentificaoresRespuestaWS>     
   var value= FieldUtils.readField(b, "value", true); //obtenerIdentificaoresRespuestaWS
   String codigo=(String)(FieldUtils.readField(value, "codigo", true));
   String descripcion=(String)(FieldUtils.readField(value, "descripcion", true));
   System.out.println(descripcion);
   if (! codigo.equals("00") ){
    System.out.println("Error " + codigo + ": " + descripcion);
   }
   else {
    int ii=0;
    var aIden=FieldUtils.readField(value, "identificadores", true);
    List<Integer> lIden=(List<Integer>) FieldUtils.readField(aIden, "item", true);
    System.out.println(lIden.size());
    for (Integer iden: lIden) {
     System.out.println(ii+ " --> + iden=" + iden);
    } 
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 } 
}


In this case, it seems that the static way is shorter and easier to use, and the "utility class" is not used. In this particular case (as it was reported in the previous post), the problem was the "AnyTypeArray" class and the buggy way Apache CXF implements it. Once this problem is solved, the dynamic way would get rid of the generation of the scaffolding of the "https.ssweb_seap_minhap_es.demoorve" package. Although this is not a serious problem as we can delete all classes from this package except "AnyTypeArray" class. Note that this class needs to be updated as mentioned in the previous post.

This post is getting a bit long. But our solution has not bin com completed. We need to call the next WS operation "obtenerRegistro", but this is a bit tricky. So before invoking this operation, I think it's better studying a bit more the possibilities of Apache CXF.


Comments

Popular posts from this blog

ORVE WS (Dynamic) (4) Jackson XML mapper

AutoFirma ins and outs (4). Errors in tests in afirma-core, jmulticard-jse

Creating a WS Client consumer