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;
 }

}

Thursday, December 24, 2015

HornetQ unable to validate the user

Oh shit!! Error again.
[WARNING] 
java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:606)
 at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
 at java.lang.Thread.run(Thread.java:745)
Caused by: javax.jms.JMSSecurityException: HQ119031: Unable to validate user: null
 at org.hornetq.core.protocol.core.impl.ChannelImpl.sendBlocking(ChannelImpl.java:399)
 at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSessionInternal(ClientSessionFactoryImpl.java:880)
 at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSessionInternal(ClientSessionFactoryImpl.java:789)
 at org.hornetq.core.client.impl.ClientSessionFactoryImpl.createSession(ClientSessionFactoryImpl.java:324)
 at org.hornetq.jms.client.HornetQConnection.authorize(HornetQConnection.java:654)
 at org.hornetq.jms.client.HornetQConnectionFactory.createConnectionInternal(HornetQConnectionFactory.java:676)
 at org.hornetq.jms.client.HornetQConnectionFactory.createQueueConnection(HornetQConnectionFactory.java:119)
 at org.hornetq.jms.client.HornetQConnectionFactory.createQueueConnection(HornetQConnectionFactory.java:114)
 at org.huahsin.jms1.MetaData.main(MetaData.java:30)
 ... 6 more
Caused by: HornetQException[errorType=SECURITY_EXCEPTION message=HQ119031: Unable to validate user: null]
 ... 15 more
This time is HornetQ is unable to validate the user. I was running some test code on JMS which shows this:
 public static void main(String[] args) {
  Context context = null;
  QueueConnection qConn = null;
  final Properties env = new Properties();

  env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
  env.put(Context.PROVIDER_URL, System.getProperty(Context.PROVIDER_URL, "remote://localhost:4447"));
  env.put(Context.SECURITY_PRINCIPAL, System.getProperty("username", "quickstartUser"));
  env.put(Context.SECURITY_CREDENTIALS, System.getProperty("password", "quickstartPwd1!"));

  context = new InitialContext(env);
  String connStr = System.getProperty("connection.factory", "jms/RemoteConnectionFactory");
  QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup(connStr);
  qConn = factory.createQueueConnection();

  ...
  ...
 }
Notice the createQueueConnection() taking empty arguments which cause this error. As of this writing, I'm using the following JMS maven configuration:
  <dependency>
    <groupId>javax.jms</groupId>
    <artifactId>jms</artifactId>
    <version>1.1</version>
   </dependency>
Actually, there is an overload method which taking additional two parameters that could pass the authentication. What frustrates me is that the intellisense only shows createQueueConnection(String arg0, String arg1). This shows the programmer is very a very irresponsible person. OK! Enough for that. By passing in the username and password, this would solve the problem.

Somehow there is a workaround for this, disable the HornetQ security in standalone-full.xml:
<hornetq-server>
    …
    <security-enabled>false</security-enabled>
</hornetq-server>
But I can't just so irresponsible to disable the security, right? I opt-out this workaround.

Better solution to exec:java in Maven

Hey! There is a simpler solution to execute a java program in Maven. Tracking back on my last development journey, there is a complicated way of doing this. Actually, I don't need any other except this:
<plugins>
      <plugin>
       <groupId>org.codehaus.mojo</groupId>
       <artifactId>exec-maven-plugin</artifactId>
       <version>1.4.0</version>
       <executions>
         <execution>
          <id>Meta</id>
          <goals>
            <goal>java</goal>
          </goals>
         </execution>
       </executions>
       <configuration>
         <mainClass>org.huahsin.jms1.QBorrower</mainClass>
       </configuration>
      </plugin>
      ...
      ...
</plugins>
And then just hit clean compile exec:java in Eclipse goal to execute the Java program.

Thursday, December 17, 2015

Wrong component? EJB can't used as web component?

Shit!! Something was wrong! When I deploy my web app into JBoss, it throws me this error:
(MSC service thread 1-2) MSC000001: Failed to start service jboss.deployment.unit."ejbWeb1.war".PARSE: org.jboss.msc.service.StartException in service jboss.deployment.unit."ejbWeb1.war".PARSE: JBAS018733: Failed to process phase PARSE of deployment "ejbWeb1.war"
 at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:127) [jboss-as-server-7.3.4.Final-redhat-1.jar:7.3.4.Final-redhat-1]
 at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1811) [jboss-msc-1.0.4.GA-redhat-1.jar:1.0.4.GA-redhat-1]
 at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1746) [jboss-msc-1.0.4.GA-redhat-1.jar:1.0.4.GA-redhat-1]
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_79]
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_79]
 at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_79]
Caused by: java.lang.RuntimeException: JBAS018043: org.huahsin.ejb.Authentication has the wrong component type, it cannot be used as a web component
 at org.jboss.as.web.deployment.component.WebComponentProcessor.deploy(WebComponentProcessor.java:120)
 at org.jboss.as.server.deployment.DeploymentUnitPhaseService.start(DeploymentUnitPhaseService.java:120) [jboss-as-server-7.3.4.Final-redhat-1.jar:7.3.4.Final-redhat-1]
 ... 5 more
The clue is org.huahsin.ejb.Authentication can not be used as web component. Ok! Fine. What is the root cause of this? I'm gonna find out this...

I got an EJB code as stated below:
package org.huahsin;

@Remote
public interface AuthenticationRemote {
    public int status();
}

package org.huahsin;

@Stateless(name="authenticate")
@LocalBean
public class Authentication implements AuthenticationRemote {
    @Override
    public int status() {
        return 1234;
    }
}
This code was located in a standalone EJB project. And then I have another set of code which will accessing the EJB code:
package org.huahsin;

@WebServlet("/authentication")
public class Authentication extends HttpServlet {

 @EJB
 private AuthenticationRemote authenticationRemote;

 public Authentication() {
  super();
 }

 @Override
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
  int result = authenticationRemote.status();
  ...
  ...
 }
 
 @Override
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
  
 }
}
Yet, this is another web app project. So, to make these two projects work together, I did the following configuration on the web project:
  1. Reference the EJB project in Project References.
  2. Add EJB project in Web Deployment Assembly.
With the code flat on the plane, I think I have spotted the error. Notice that both web and EJB project having the same class name (Authentication) and package name (org.huahsin), this is where the error came from. Thus, rework on the code by placing org.huahsin.ejb for EJB project and org.huahsin.web for web project will solve this issue.

Tuesday, December 15, 2015

Filter file name with Boost regular expression

The story of the use case is like this; given a file name having the pattern, dummyx.txt, where x is a running number, how could I effectively filter out those doesn't match with this pattern? My initial answer would be this:
set<wstring> files = ... // assuming a list of file name are return and kept in the variable files

set<wstring>::iterator it;

for( it=list.begin(); it != list.end(); it++ ) {
    if ((*it).find(L"dummy.txt") != string::npos) {
        /***** proceed with the flow *****/
    }
}
I just feel that this piece is so rough. Instead of comparing the file name using wildcard search, I start to think of a more elegant way to get this done. So the first idea pops up in my mind is to use regular expression, here come to the second revision of the piece:
boost::regex expr("([\\:/\\\\\\w]+)?dummy(\\d{1,3})(\\.txt)$");
set<wstring>::iterator it;

for( it=list.begin(); it != list.end(); it++ ) {
    string s((*it).begin(), (*it).end());
    if (boost::regex_match(s, expr)) {
        /***** proceed with the flow *****/
    }
}
Much better? At least the code look much more elegant than the previous piece. Not only that it will filter out those doesn't match with the pattern, it will also filter out the name with only dummy in the file name.