Showing posts with label webservice. Show all posts
Showing posts with label webservice. Show all posts

Friday, July 6, 2018

Rebuild gSOAP from the source

I'm making a web service in C++. Never try it before, just want to find out how hard could it be? Unlike JAVA, the web service library in C++ isn't that popular, however, I still manage to get one. It is called gSOAP, I download it from sourceForge. And the experts are putting a thumbs up on it too.

For the first time, I am doing it on the Windows platform, it was working just fine. When I move on to the Linux machine, I got the following error pop up when I'm compiling my source:
In file included from soapH.h:16:0,
                 from HuahsinServiceBindingPort.nsmap:2,
                 from main.cpp:3:
soapStub.h:24:3: error: #error "GSOAP VERSION 20828 MISMATCH IN GENERATED CODE VERSUS LIBRARY CODE: PLEASE REINSTALL PACKAGE"
 # error "GSOAP VERSION 20828 MISMATCH IN GENERATED CODE VERSUS LIBRARY CODE: PLEASE REINSTALL PACKAGE"
   ^~~~~
What's happening just now? The error seems trying to remind me that the gSoap version isn't compatible with something else. This never happened in Windows. Wouldn't it be the cause of gSOAP I downloaded is for Windows only? Could it be some other thing else? Flashing back my memory what I did wrong, I downloaded the gSOAP from sourceForge, and then at the same time, I downloaded another version from Fedora repository. Then I generate the stub objects using the following command with Fedora's version, like this:
wsdl2h -o hello.h http://localhost:8080/services/hello?wsdl -t/usr/share/gsoap/WS/typemap.dat
soapcpp2 -jCL -I/usr/share/gsoap/import hello.h
And then I compile the source code with the stdsoap2.cpp from sourceForge's version.
g++ -g -o main.exe main.cpp soapC.cpp soapHuahsinServiceBindingPortProxy.cpp 
/home/kokhoe/tool/gsoap-2.8.68/gsoap/stdsoap2.cpp 
-I/home/kokhoe/tool/gsoap-2.8.68/gsoap 
-I. -std=c++11
Is this the reason why the compilation failed? Am I correct? I wasn't sure whether my assumption makes sense. As I read on the documentation, it does mention this:
This error is caused by mixing old with new gSOAP versions of the generated code and gSOAP libraries. Make sure to include the latest stdsoap2.h and link with the latest libgsoapXX.a libraries (or use the stdsoap2.c or stdsoap2.cpp source code).
I think this has given a clear sign on my mistake. I knew that I am going to make a fresh build from source, but still trying hard to find a lazy way, without making any hassle work to rebuild from the source. I am making a deep breath... thinking... and then keep on searching... for quite some time... and I found a step by step manual guide that could help me from building the source until the installation. It is accompanied with the source I downloaded. Thus, I made a bold decision to rebuild everything, including gSOAP.

During the build process, there are many libraries were missing, among those errors I have, following error tells no clue on what was missing. The compiler just stops like that with the given command like this:
gcc -DWITH_YACC -DWITH_FLEX -DSOAPCPP2_IMPORT_PATH="\"/usr/local/share/gsoap/import\"" 
-DLINUX -g -O2 -o soapcpp2 soapcpp2-soapcpp2_yacc.o soapcpp2-soapcpp2_lex.o soapcpp2-symbol2.o soapcpp2-error2.o 
soapcpp2-init2.o soapcpp2-soapcpp2.o -ly  
/usr/bin/ld: cannot find -ly
It gave me a hard time to identify the relationship between the -DWITH_YACC, -DWITH_FLEX, and -ly. There are the Bison and Flex libraries actually. Thank god. The rebuild process continues at last and my source was built with no error.

Friday, June 29, 2018

Invocation WsimportTool failed?!!

Hey there, I am making a new friend today. The meet this new friend in Eclipse Photon when I was creating a brand new web service project. Here is the story, when I first started the project, I only have the WSDL file ready in the source folder, and then I have the jaxws-maven-plugin in my pom file, like this:
  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.5</version>
    <executions>
      <execution>
        <goals>
          <goal>wsimport</goal>
        </goals>
        <configuration>
          <keep>true</keep>
          <extension>true</extension>
          <sourceDestDir>${basedir}/src/main/java</sourceDestDir>
          <packageName>org.huahsin68.ws</packageName>
          <wsdlDirectory>${basedir}/src/main/resources</wsdlDirectory>
          <wsdlFiles>
            <wsdlFile>hello.wsdl</wsdlFile>
          </wsdlFiles>
        </configuration>
      </execution>
    </executions>
  </plugin>
Then this error immediately shown up right after the execution tag as shown below. What was that mean? I don’t even understand why the WsimportTool could fail?
Invocation of com.sun.tools.ws.wscompile.WsimportTool failed - check output (org.codehaus.mojo:jaxws-maven-plugin:2.5:wsimport:default:generate-sources)

org.apache.maven.plugin.MojoExecutionException: Invocation of com.sun.tools.ws.wscompile.WsimportTool failed - check output
 at org.codehaus.mojo.jaxws.AbstractJaxwsMojo.exec(AbstractJaxwsMojo.java:485)
 at org.codehaus.mojo.jaxws.WsImportMojo.processLocalWsdlFiles(WsImportMojo.java:339)
 at org.codehaus.mojo.jaxws.WsImportMojo.executeJaxws(WsImportMojo.java:292)
 at org.codehaus.mojo.jaxws.MainWsImportMojo.executeJaxws(MainWsImportMojo.java:54)
 at org.codehaus.mojo.jaxws.AbstractJaxwsMojo.execute(AbstractJaxwsMojo.java:386)
 at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
 at org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:331)
 at org.eclipse.m2e.core.internal.embedder.MavenImpl$11.call(MavenImpl.java:1362)
 at org.eclipse.m2e.core.internal.embedder.MavenImpl$11.call(MavenImpl.java:1)
 at org.eclipse.m2e.core.internal.embedder.MavenExecutionContext.executeBare(MavenExecutionContext.java:177)
 at org.eclipse.m2e.core.internal.embedder.MavenExecutionContext.execute(MavenExecutionContext.java:112)
 at org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:1360)
 at org.eclipse.m2e.core.project.configurator.MojoExecutionBuildParticipant.build(MojoExecutionBuildParticipant.java:52)
 at org.eclipse.m2e.core.internal.builder.MavenBuilderImpl.build(MavenBuilderImpl.java:137)
 at org.eclipse.m2e.core.internal.builder.MavenBuilder$1.method(MavenBuilder.java:172)
 at org.eclipse.m2e.core.internal.builder.MavenBuilder$1.method(MavenBuilder.java:1)
 at org.eclipse.m2e.core.internal.builder.MavenBuilder$BuildMethod$1$1.call(MavenBuilder.java:115)
 at org.eclipse.m2e.core.internal.embedder.MavenExecutionContext.executeBare(MavenExecutionContext.java:177)
 at org.eclipse.m2e.core.internal.embedder.MavenExecutionContext.execute(MavenExecutionContext.java:112)
 at org.eclipse.m2e.core.internal.builder.MavenBuilder$BuildMethod$1.call(MavenBuilder.java:105)
 at org.eclipse.m2e.core.internal.embedder.MavenExecutionContext.executeBare(MavenExecutionContext.java:177)
 at org.eclipse.m2e.core.internal.embedder.MavenExecutionContext.execute(MavenExecutionContext.java:151)
 at org.eclipse.m2e.core.internal.embedder.MavenExecutionContext.execute(MavenExecutionContext.java:99)
 at org.eclipse.m2e.core.internal.builder.MavenBuilder$BuildMethod.execute(MavenBuilder.java:86)
 at org.eclipse.m2e.core.internal.builder.MavenBuilder.build(MavenBuilder.java:200)
 at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:795)
 at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
 at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:216)
 at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:259)
 at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:312)
 at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
 at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:315)
 at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:367)
 at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:388)
 at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:142)
 at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:232)
 at org.eclipse.core.internal.jobs.Worker.run(Worker.java:60)
Googling the around some said that this is a defect from the IDE; some even said that this could be caused by the JAVA version issue; some even said JDK 8 is not supported in Eclipse Mars version. Does it true? Not really. Firstly, I issued a command mvn clean and then follow by mvn generate-sources. The error is gone! What a weird day?!!

Tuesday, June 19, 2018

Making a contract first web service with Spring Boot

I have been told that Spring Boot is a great tool to learn. What do I think? In order to get my hand dirty on that, I've chosen to use it to develop a web service project. Well, I'd spend almost an hour plus to launch a web service, able to accept a request and provide a response. It was really damn amazing.

Unlike the tutorial I found on the Internet, my job scope doesn't work in that way. It is the WSDL file was created first, a.k.a. contract-first, but the tutorial I found on the Internet are contract-last. Wait, there are still hope, I found following sample that could help out.

Sample 1
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter
{
   @Bean(name = "services")
   public Wsdl11Definition defaultWsdl11Definition() {
      SimpleWsdl11Definition wsdl11Definition = new SimpleWsdl11Definition();
      wsdl11Definition.setWsdl(new ClassPathResource("/schema/MyWsdl.wsdl")); //your wsdl location
      return wsdl11Definition;
   }
}
Sample 2
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter
{
   @Bean(name = "wsdlname")
   public DefaultWsdl11Definition defaultWsdl11Definition (XsdSchema cityRequestSchema) {
      DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
      wsdl11Definition.setRequestSuffix("ByCountry");
      wsdl11Definition.setResponseSuffix("City");
      wsdl11Definition.setPortTypeName("Hotelport");
      wsdl11Definition.setLocationUri("/ProjectName");
      wsdl11Definition.setTargetNamespace("http://spring.io/guides/gs-producing-web-service");
      wsdl11Definition.setSchema(cityRequestSchema);
      return wsdl11Definition;
   }

   @Bean
   public XsdSchema cityRequestSchema() {
      return new SimpleXsdSchema(new ClassPathResource("CityRequest.xsd"));
   }
}
Wait, I'm looking at the DefaultWsdl11Definition class, which I felt kind of weird, it doesn't seem sound right to me. Is it going to create the WSDL specification version 1.1, but my version is 1.2. I don't think this will work. Wait, the story has not ended, after a few days of searching, I manage to find a tutorial which has done a very good job on this matter. There are 2 ways to do this:

First solution
Do it in Spring configuration and load this configuration in the main class.
<beans xmlns="http://www.springframework.org/schema/beans" 
 xmlns:context="http://www.springframework.org/schema/context" 
 xmlns:jaxws="http://cxf.apache.org/jaxws" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                     http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
                     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

   <context:component-scan base-package="org.huahsin68.core"/>

   <jaxws:endpoint 
      id="HelloService" 
      implementor="#helloResponseImpl"
      serviceName="s:HelloService"
      address="/hello"
      xmlns:s="http://huahsin68.org/helloService>
      <jaxws:features>
         < bean class="org.apache.cxf.ext.logging.LoggingFeature"/>
      </jaxws:features>
   </jaxws:endpoint> 
</beans>
@SpringBootApplication
@ImportResource({"classpath:beans.xml"})
public class Application {
   ...
   ...
}
Second solution
Do it in Java. Create an Endpoint bean inside the configuration bean.
@Configuration
public class WebServiceConfig {

   ...
   ...

   @Bean
   public Endpoint endpoint() {
      HelloService h = new HelloService();
      EndpointImpl endpoint = new EndpointImpl(springBus(), new InfoServiceImpl());

      endpoint.getFeatures().add(new LoggingFeature());
      endpoint.setWsdlLocation("classpath:hello.wsdl");
      endpoint.setServiceName(h.getServiceName());
      endpoint.publish("/hello");

      return endpoint;
   }
}

Reference:
Building soap web service with Apache CXF and Spring Boot

Wednesday, August 23, 2017

A WSDL requires input for processing

Although I have the WSDL successfully loaded into SoapUI, but the structure was incorrect. Following is the SOAP request message generated by default when the WSDL first loaded into SoapUI:
<soap:envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
   <soap:header/>
   <soap:body>
</soap:Envelope>
I'll get an error message says line -1: null when I validate on this request message. To fix this error, I must have at least one element in the body tag. Here is the new WSDL code for the input:
...

<wsdl:types>
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://helloWorld.huahsin.org/">
    <xsd:element name="Input"/>
  </xsd:schema>
</wsdl:types>

< wsdl:message name="HelloWorldRequest">
  < wsdl:part name="parameters" element="tns:Input"/>
< /wsdl:message>
...
Now the SOAP request message will look like this:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:hel="http://helloWorld.huahsin.org/">
   <soap:Header/>
   <soap:Body>
      <hel:Input>?</hel:Input>
   </soap:Body>
</soap:Envelope>
In the real world, it is impossible to have this empty SOAP request message, some input would require to pass in for processing. For the sake of this experiment, I would need a name and an age field. The name field is a mandatory field, whereas age field is optional.
...

<wsdl:types>
  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://helloWorld.huahsin.org/">
    <xsd:element name="Input">
      <xsd:complexType>
        <xsd:sequence>
          <xsd:element name="name" type="xsd:string" minOccurs="1"/>
          <xsd:element name="age" type="xsd:integer" minOccurs="0"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
</wsdl:types>
With this new WSDL design, the SOAP message now will be much better and complete:
< soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:hel="http://helloWorld.huahsin.org/">
   <soap:Header/>
   <soap:Body>
      <hel:Input>
         <name>?</name>
         <!--Optional:-->
         <age>?</age>
      </hel:Input>
   </soap:Body>
</soap:Envelope>

Tuesday, August 8, 2017

Illegal extension attribute 'soapAction'

I got it up!! Finally, I got my first dummy WSDL, first created with notepad, load into SoapUI. Since there is no tool could allow me to do it in easy way, I have to verify the syntax with my own eyes.

On my first try, I got this stupid error when I first load into SoapUI. The cause of the error happened in wsdl:operation and soapAction is an illegal attribute? Ehrrr~ What is that means?
WSDLException (at /wsdl:definitions/wsdl:binding/wsdl:operation): faultCode=INVALID_WSDL: Encountered illegal extension attribute 'soapAction'. Extension attributes must be in a namespace other than WSDL's.    
I take a closer look into my first baby, then only I notice that I have mistakenly mess up the wsdl:operation with soapAction. This is something bad without a good tool to gauge my work.
<wsdl:binding name="HelloWorldSOAP12" type="tns:HelloWorldPort">
  <soap12:binding transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="HelloWorld" soapAction="http://helloWorld.huahsin68.org/helloWorld"/>
  <wsdl:input>
    <soap12:body use="literal"/>
  <wsdl:output>
    <soap12:body use="literal"/>
  </wsdl:output>
</wsdl:binding>
The fix is as follows:
<wsdl:binding name="HelloWorldSOAP12" type="tns:HelloWorldPort">
  <soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="HelloWorld">
    <soap12:operation soapAction="HelloWorldAction"/>
    <wsdl:input>
      <soap12:body use="literal"/>
    </wsdl:input>
    <wsdl:output>
      <soap12:body use="literal"/>
    </wsdl:output>
  </wsdl:operation>
</wsdl:binding>

Saturday, December 24, 2016

Generate WSDL file with wsgen

Today I'm running a Web Service experiment again. But I just feel that I'm making a tutorial right now. No way, this is just a finding on my silly mistake though. Assuming I have the following code setup:
package org.huahsin.main.bo;

...

@WebService(targetNamespace = "http://main.huahsin.org/")
public interface IPersonService {

 @WebMethod
 public String getPerson(String name);
}


package org.huahsin.main;

...

@WebService(serviceName = "PersonService", 
   endpointInterface = "org.huahsin.main.bo.IPersonService",
   portName = "PersonServicePort")
public class PersonService implements IPersonService {

 @Override
 public String getPerson(String name) {
  return "Hello World " + name;
 }

}
Take good care of the endpointInterface, I mess up this name with the implementation class name, that suppose belong to an Interface. Next is to fire up the WSDL through HTTP. Previously I was using this trick to make a WSDL file:
public class PersonClient {

 public static void main(String[] args) {
  Endpoint.publish("http://localhost:8080/ws", new PersonService());
 }
}
And then hit the URL http://localhost:8080/ws?wsdl in the browser, copy the content in the WSDL file. Actually wsgen has already covered the WSDL file generation with -wsdl as shown following command:

wsgen -keep -wsdl -cp ./WebContent/WEB-INF/classes org.huahsin.main.PersonService

There will be some minor modification on the REPLACE_WITH_ACTUAL_URL after WSDL file is generated:
  <service name="PersonService">
    <port binding="tns:PersonServicePortBinding" name="PersonServicePort">
      <soap:address location="REPLACE_WITH_ACTUAL_URL"/>
    </port>
  </service>
Just replace any URL as you like, make sure the IP and port are accessible, it is very useful to determine what are the service available currently deployed.

* Do take note that when I deploy this code into WebSphere Liberty Profile (v8.5.5.8 as of this writing) with CXF runtime bundle with my code, it causes conflict error. According to the documentation, I should not enable jaxws-2.2 feature.
If you application provides its own copy of CXF JAR files as the application libraries, for example, in the WEB-INF/lib directory of a web application, you cannot enable the jaxws-2.2 feature in the server.xml file.

Thursday, December 31, 2015

Two classes have the same XML type name?

When I'm generating the SEI stub object from a WSDL using following command:
   <plugin>
       <groupId>org.codehaus.mojo</groupId>
       <artifactId>jaxws-maven-plugin</artifactId>
       <executions>
         <execution>
           <goals>
             <goal>wsimport</goal>
           </goals>
             <configuration>
             <wsdlUrls>
               <wsdlUrl>http://localhost:8080/ws2?wsdl</wsdlUrl>
             </wsdlUrls>
           <keep>true</keep>
           <packageName>org.huahsin.jaxws.staff</packageName>
           <sourceDestDir>${basedir}/src</sourceDestDir>             
         </configuration>
         </execution>
       </executions>
   </plugin>
I'm not happy with the package name, so I decide to give a better name by changing the package name in a manual way. But when the client program trying to access the service:
 URL url = new URL("http://localhost:8080/ws2/SeniorManager?wsdl");
 QName qname = new QName("http://staff.huahsin.org/", "SeniorManagerService");
 SeniorManagerService service = new SeniorManagerService(url, qname);
 ISeniorManager manager = service.getSeniorManagerPort();
 System.out.println(manager.getID());
I hit this error:
Exception in thread "main" javax.xml.ws.WebServiceException: Unable to create JAXBContext
 at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:156)
 at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.postProcess(AbstractSEIModelImpl.java:84)
 at com.sun.xml.internal.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:234)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.createSEIPortInfo(WSServiceDelegate.java:673)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.addSEI(WSServiceDelegate.java:661)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:330)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:313)
 at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:295)
 at javax.xml.ws.Service.getPort(Service.java:119)
 at org.huahsin.jaxws.SeniorManagerService.getSeniorManagerPort(SeniorManagerService.java:72)
 at org.huahsin.Client.main(Client.java:23)
Caused by: java.security.PrivilegedActionException: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "{http://staff.huahsin.org/}getID". Use @XmlType.name and @XmlType.namespace to assign different names to them.
 this problem is related to the following location:
  at org.huahsin.jaxws.GetID
  at public javax.xml.bind.JAXBElement org.huahsin.jaxws.ObjectFactory.createGetID(org.huahsin.jaxws.GetID)
  at org.huahsin.jaxws.ObjectFactory
 this problem is related to the following location:
  at org.huahsin.jaxws.staff.GetID
Two classes have the same XML type name "{http://staff.huahsin.org/}getIDResponse". Use @XmlType.name and @XmlType.namespace to assign different names to them.
 this problem is related to the following location:
  at org.huahsin.jaxws.GetIDResponse
  at public javax.xml.bind.JAXBElement org.huahsin.jaxws.ObjectFactory.createGetIDResponse(org.huahsin.jaxws.GetIDResponse)
  at org.huahsin.jaxws.ObjectFactory
 this problem is related to the following location:
  at org.huahsin.jaxws.staff.GetIDResponse

 at java.security.AccessController.doPrivileged(Native Method)
 at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:143)
 ... 10 more
Caused by: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "{http://staff.huahsin.org/}getID". Use @XmlType.name and @XmlType.namespace to assign different names to them.
 this problem is related to the following location:
  at org.huahsin.jaxws.GetID
  at public javax.xml.bind.JAXBElement org.huahsin.jaxws.ObjectFactory.createGetID(org.huahsin.jaxws.GetID)
  at org.huahsin.jaxws.ObjectFactory
 this problem is related to the following location:
  at org.huahsin.jaxws.staff.GetID
Two classes have the same XML type name "{http://staff.huahsin.org/}getIDResponse". Use @XmlType.name and @XmlType.namespace to assign different names to them.
 this problem is related to the following location:
  at org.huahsin.jaxws.GetIDResponse
  at public javax.xml.bind.JAXBElement org.huahsin.jaxws.ObjectFactory.createGetIDResponse(org.huahsin.jaxws.GetIDResponse)
  at org.huahsin.jaxws.ObjectFactory
 this problem is related to the following location:
  at org.huahsin.jaxws.staff.GetIDResponse

 at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:442)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:274)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:125)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1127)
 at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:173)
 at com.sun.xml.internal.bind.api.JAXBRIContext.newInstance(JAXBRIContext.java:95)
 at com.sun.xml.internal.ws.developer.JAXBContextFactory$1.createJAXBContext(JAXBContextFactory.java:98)
 at com.sun.xml.internal.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:151)
 at com.sun.xml.internal.ws.model.AbstractSEIModelImpl$1.run(AbstractSEIModelImpl.java:143)
 ... 12 more
At first, this was really clueless to me. Spending day and night searching whatever clue appears on the stacktrace. And eventually I found nothing. Until in the late night only I found the out root cause. Never change the stub object's package name by hand, if the it does make some noise on you, delete the whole thing and regenerate again.

Like it or not, this is the way.

JAXWS and EJB can live together

If the SEI code were accessible from the client within the same package, do I still need the following code in order to access the service code?
   URL url = new URL("http://localhost:8080/ws1?wsdl");
   QName qname = new QName("http://webmethod.huahsin.org/", "HelloWorldImplService");
   HelloWorldImplService service = new HelloWorldImplService(url, qname);
   IHelloWorld manager = service.getHelloWorldImplPort();
   System.out.println(manager.sayHelloWorld());
Imaging I have the following SEI code and there are sit in the same application:
package org.huahsin.webmethod;
 
…
 
@WebService
@SOAPBinding(style=Style.DOCUMENT)
public interface IHelloWorld {
 
 @WebMethod
 String sayHelloWorld();
}
 
 
package org.huahsin.webmethod;
 
…
 
@WebService(endpointInterface="org.huahsin.webmethod.IHelloWorld")
public class HelloWorldImpl implements IHelloWorld {
 
 @Override
 public String sayHelloWorld() {
  return "Hello World";
 }
 
}
I just feel a bit weird in doing this since both client code and server code are live in the same application. I did a search on the forum and got to know that web service code can be accessed through EJB. Just "top up" the @Stateless and @Remote to the SEI and we are done.
@Remote
@WebService
@SOAPBinding(style=Style.DOCUMENT)
public interface IHelloWorld {
 
 @WebMethod
 String sayHelloWorld();
}
 
 
package org.huahsin.webmethod;
 
…

@Stateless
@WebService(endpointInterface="org.huahsin.webmethod.IHelloWorld")
public class HelloWorldImpl implements IHelloWorld {
 
 @Override
 public String sayHelloWorld() {
  return "Hello World";
 }
 
}
To access the web service code in EJB way, do the following (excuse myself, I'm doing it in JSF bean):
@ManagedBean
@RequestScoped
public class HelloWorldController {

 @EJB
 private IHelloWorld helloWorld;
 ...
 ...
Sound cool? The best part of this is no more generating stub code.

Saturday, December 26, 2015

Asynchronous web service is a real thing

It has been so long for my wish to come true. And finally, it is proven that asynchronous web service can be done. First, I make a regular SEI:
package org.huahsin.webmethod;

…

@WebService
@SOAPBinding(style=Style.DOCUMENT)
public interface IHelloWorld {

 @WebMethod
 String sayHelloWorld();
}


package org.huahsin.webmethod;

…

@WebService(endpointInterface="org.huahsin.webmethod.IHelloWorld")
public class HelloWorldImpl implements IHelloWorld {

 @Override
 public String sayHelloWorld() {
  return "Hello World";
 }

}
And then construct the following wsimport plugin in POM.xml to generate Java artifact. But this will only generate synchronous method.
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jaxws-maven-plugin</artifactId>
        <executions>
            <execution>
                <goals>
                    <goal>wsimport</goal>
                </goals>
                <configuration>
                    <wsdlUrls>
                        <wsdlUrl>http://localhost:8080/ws1?wsdl</wsdlUrl>
                    </wsdlUrls>
                    <bindingDirectory>${basedir}/resources/jaxws</bindingDirectory>
                    <keep>true</keep>
                    <packageName>org.huahsin.jaxws</packageName>
                    <sourceDestDir>${basedir}/src</sourceDestDir>             
                </configuration>
            </execution>
        </executions>
    </plugin>
To generate asynchronous method, I would require additional file to bind onto wsimport. It is just a regular XML file located at the path where <buildingDirectory> is pointing to, and the content is as shown in below:
<bindings
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="http://localhost:8080/ws1?wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">

    <!-- applies to wsdl:definitions node, that would mean the entire wsdl -->
    <enableAsyncMapping>false</enableAsyncMapping>

    <!-- wsdl:portType operation customization -->
    <bindings node="wsdl:definitions/wsdl:portType [@name='IHelloWorld']/wsdl:operation[@name='sayHelloWorld']">
        <enableAsyncMapping>true</enableAsyncMapping>
    </bindings>
  
</bindings>
It's job scope is to locate the sayHelloWorld method using XPath and then convert this method to support asynchronous. While not affecting to the other's behaviour, I remain the rest of the methods as synchronous. Once everything is ready, I would first need a publisher to start the engine before I can do the conversion:
public class Publisher {
 public static void main(String args[]) {
  Endpoint.publish("http://localhost:8080/ws1", new HelloWorldImpl());
 }
}
Then fire the command mvn compile to generate Java artifact. This will have additional two methods generated for asynchronous (as shown in the code snippet below) compare to the default generation.
public interface IHelloWorld {
    public Response<SayHelloWorldResponse> sayHelloWorldAsync();

    public Future<?>sayHelloWorldAsync(
        @WebParam(name = "asyncHandler", targetNamespace = "")
        AsyncHandler<SayHelloWorldResponse> asyncHandler);

    ...
}
Now, to test my code is really works? I would have this simple program firing the asynchronous method:
...

import org.huahsin.jaxws.HelloWorldImplService;
import org.huahsin.jaxws.IHelloWorld;
import org.huahsin.jaxws.SayHelloWorldResponse;

...


public class Client {
 static private String msg = "";
 
 public static void main(String[] args) … {
  System.out.println("before: " + msg);
  e.sayHelloWorldMethod();
  Thread.sleep(1000);
  System.out.println("after: " + msg);
 }

 private void sayHelloWorldMethod() throws MalformedURLException, InterruptedException, ExecutionException {
  URL url = new URL("http://localhost:8080/ws1?wsdl");
  QName qname = new QName("http://webmethod.huahsin.org/", "HelloWorldImplService");
  HelloWorldImplService service = new HelloWorldImplService(url, qname);
  IHelloWorld hello = service.getHelloWorldImplPort();
  
  Response res = hello.sayHelloWorldAsync();
  SayHelloWorldResponse output = (SayHelloWorldResponse) res.get();
  output.getReturn();
  
  hello.sayHelloWorldAsync(new AsyncHandler() {

   @Override
   public void handleResponse(Response res) {
    try {
     setMessage(((SayHelloWorldResponse)res.get()).getReturn());
    }
    catch (InterruptedException | ExecutionException e) {
     e.printStackTrace();
    }
   }
   
  });
 }

 private void setMessage(String msg) {
   this.msg = msg;
 }

}

Sunday, October 18, 2015

I'm so confused with WSDL

I’m confused, I’m screwed.

I’m confused because there seems so many things need to be done in order to generate a WSDL file. I need to create the POJO class; I need to create the XSD; I need to build the WSDL and link XSD together; I need to create publisher class to publish the web service. And then generate the JAX-WS JAVA artifacts… No, I don’t need that at the moment. Let’s see how I begin this mess.

With bottom up approach, one way to construct the JAX-WS Java artifacts is through the Endpoint class. Like what I did with the following code:
@WebService(serviceName="HelloWorldService", portName="HelloWorldPort", endpointInterface="org.huahsin.ws.HelloWorld")
public class HelloWorldEndpoint {
 public static void main(String args[]) {
  HelloWorld inst = new HelloWorld();
  Endpoint.publish("http://localhost:8080/wsAsync", inst);
  System.out.println("service published");
 }
}
Assuming I have the HelloWorld POJO declared in this way:
@WebService(serviceName="HelloWorldService")
public class HelloWorld {
 @WebMethod(operationName="hello")
 public String hello(@WebParam(name="name") String name) {
  return "Hello " + name;
 }
}
Executes the program, send the URL (http://localhost:8080/wsAsync?wsdl) to the web browser, then the WSDL content will be shown. After that save the WSDL content, by selecting all the text in the web page, and paste it into notepad, then save it as a file with WSDL file extension. Anyhow, this isn’t a good approach to generate the WSDL file.

Actually, there is a better approach for doing it with wsgen command. The usage is as shown below:

C:\project\ws>wsgen -verbose -wsdl -keep -cp target\classes org.huahsin.HelloWorld

Don’t confuse with –cp option, it is referring to the path where the compiled class of org.huahsin.HelloWorld is stored (I thought it was the JDK path). And the –wsdl option is to tell the wsgen command to generate a WSDL file. I feel much better with the tool now. Besides that, JBoss does have the similar utility, named wsprovide, and the usage is as follow:

C:\project\ws>wsprovide -w -k -c target\classes org.huahsin.HelloWorld

But make sure the \bin is registered in environment variable before it is usable.

Thursday, October 1, 2015

wsimport wasn't covered by lifecycle configuration

I was working on a project that requires me to use Maven to generate Java artifacts from WSDL. For the first time, I'm doing it with Maven. Somehow the Eclipse m2e seem doesn’t support wsimport lifecycle, my Maven configuration is as follows:
<plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>jaxws-maven-plugin</artifactId>
   <executions>
      <execution>
            <goals>
               <goal>wsimport</goal>
            </goals>
            <configuration>
               <wsdlLocation>http://huahsin.org/wsasync</wsdlLocation>
               <wsdlDirectory>${basedir}/resources</wsdlDirectory>
               <keep>true</keep>
               <packageName>org.huahsin.ws</packageName>
               <sourceDestDir>${basedir}/src</sourceDestDir>    
            </configuration>
         </execution>
      </executions>
</plugin>
This is the original error message:
  Plugin execution not covered by lifecycle configuration: 
  org.codehaus.mojo:jaxws-maven-plugin:1.12:wsimport (execution: default, phase: generate-sources)
I though by adding the generate-sources phase into the configuration will help. But in fact, it wouldn’t.
   …
 
   <executions>
      <execution>
         <phase>generate-sources</phase>
            <goals>
   … 
Sigh~ asking myself why do I still want to scratch my head on a problem since m2e has already provided me the solution? With the POM file open in Eclipse, hover to that error, click on the Discover new m2e connectors to retrieve the jaxws-maven-plugin connector. This connector is specially designed to handle wsimport for me. Make me worry free.

Sunday, September 27, 2015

Duplicate class while generating artifacts from XSD

Given the following XSDs:

Request.xsd
 <xsd:element name="HelloWorldReqType">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element id="name" name="name" maxOccurs="1" minOccurs="1"/>
    <xsd:element id="gender" name="gender" maxOccurs="1" minOccurs="1"/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
Response.xsd
 <xsd:element name="HelloWorld_response">
  &lt xsd:complexType>
   <xsd:sequence>
    <xsd:element id="greetings" name="greetings" maxOccurs="1" minOccurs="1"/>
   </xsd:sequence>
  </xsd:complexType>
 </xsd:element>
I should not explicitly define the same set of request and response elements in WSDL while I have chosen to import them. This is what I did to the WSDL:
  <wsdl:types>
   <xsd:schema targetNamespace="http://www.example.org/HelloWorld/">
     <xsd:import namespace="http://www.example.org/HelloWorldReq" schemaLocation="../resources/HelloWorldReq.xsd"/>
     <xsd:import namespace="http://www.example.org/HelloWorldRes" schemaLocation="../resources/HelloWorldRes.xsd"/>
     <xsd:element name="helloWorld_request">
      <xsd:complexType>
       <xsd:sequence>
        <xsd:element name="name" type="xsd:string" maxOccurs="1" minOccurs="1"/>
        <xsd:element name="gender" type="xsd:string" maxOccurs="1" minOccurs="1"></xsd:element>
       </xsd:sequence>
      </xsd:complexType>
     </xsd:element>
     <xsd:element name="helloWorld_response">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="greetings" type="xsd:string" maxOccurs="1" minOccurs="1"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>    
    </xsd:schema>
  </wsdl:types>
Line 3 and 4 are the import statement that import the 2 XSDs into WSDL whereas starting from line 5, I define the same set of elements as in the XSDs. Well, this will receive an error complaining duplicate class while generating the JAVA artifact from XSD as shown below:
parsing WSDL...


[ERROR] A class/interface with the same name "org.huahsin.ws.HelloWorldResponse" is already in use. Use a class customization to resolve this conflict.
  line 23 of file:/home/kokhoe/workspace/wsAsync/resources/HelloWorld.wsdl

[ERROR] (Relevant to above error) another "HelloWorldResponse" is generated from here.
  line 8 of file:/home/kokhoe/workspace/wsAsync/resources/HelloWorldRes.xsd

[ERROR] Two declarations cause a collision in the ObjectFactory class.
  line 8 of file:/home/kokhoe/workspace/wsAsync/resources/HelloWorldRes.xsd

[ERROR] (Related to above error) This is the other declaration.   
  line 23 of file:/home/kokhoe/workspace/wsAsync/resources/HelloWorld.wsdl 
Thus experience from here was never be so greedy, choose either site when the element should define.

Tuesday, February 10, 2015

class not found in wsgen

When I have finished coding on the web service (using document style), I'm ready to generate extra classes with wsgen. Usually the following command will be used for this purpose.

wsgen -keep -cp . org.huahsin.HelloWorld

Anyhow, this command is incomplete because I receive a class not found error immediately as shown below:
C:\workspace\WebService1>wsgen -verbose -cp . org.huahsin.HelloWorld
Class not found: "org.huahsin.HelloWorld"


Usage: WSGEN [options] 

...
...

Referring to the wsgen documentation, it did mention that the -classpath or -cp is referring to an input class file, not the source file. Thus to ensure the command above work correctly, change to the root of the project directory (not bin/ or src/), then fire the command with -cp point to bin/ instead of current directory like this:

wsgen -verbose -cp ./bin org.huahsin.HelloWorld

Besides that, I notice this command doesn't keep the source files after the extra class files being generated. To keep the source files, command below would help:

wsgen -verbose -cp ./bin -s ./src org.huahsin.HelloWorld

Tuesday, August 19, 2014

Tomcat fail to start if webservices-rt were declare as provided scope in Maven

Arrghhhh! Tomcat failed to start again! And I see the same error message again!

org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost]]

For this round was cause by the webservices-rt dependency has been declare as provided scope.
  
   org.glassfish.metro
   webservices-rt
   2.2
   provided
  
Interestingly if I declare provided scope to webservices-api, the Tomcat were just run fine.