2012. november 16., péntek

JBoss AS7 JNDI and Migration from AS6

When you migrate an application from JBoss AS6 to AS7, probably you will encounter a lot of exceptions if you do not read the AS7 migration guide beforehand. :) This is also the case when naming your objects in JNDI, so if you use JNDI lookups, @Resource annotations, ejb-jar.xml or web.xml resource references etc., then make sure to read the AS7 JNDI Reference first. Also please note that in files like web.xml and ejb-jar.xml, the jndi-name element in the resource-ref seems to be mandatory, contrary to AS6 or other application servers.

    My JMS Connection Factory
    jms/MyConnectionFactory
    java:/jms/MyConnectionFactory
    javax.jms.ConnectionFactory
    Container

Keeping things like this in mind can drastically reduce the time spent on investigating deployment errors like the one below:
Unknown error

Unexpected HTTP response: 500

Request
{
    "address" => [("deployment" => "MyApp.war")],
    "operation" => "deploy"
}

Response

Internal Server Error
{
    "outcome" => "failed",
    "failure-description" => {"JBAS014771: Services with missing/unavailable dependencies" => [
        "jboss.deployment.unit.\"MyApp.war\".component.\"javax.faces.webapp.FacesServlet\".START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".jboss.security.jacc Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.MySessionBean1.START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.\"javax.servlet.jsp.jstl.tlv.ScriptFreeTLV\".START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.\"org.apache.catalina.servlets.DefaultServlet\".START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".jndiDependencyService Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.MySessionBean2.START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.\"org.apache.jasper.servlet.JspServlet\".START Missing[JBAS014861: ]",
        "jboss.naming.context.java.module.\"MyApp\".\"MyApp\".env.jms.MyConnectionFactory Missing[jboss.naming.context.java.jboss.resources.jms.MyConnectionFactory]",
        "jboss.web.deployment.default-host./myapp Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.\"org.jboss.as.weld.webtier.jsp.JspInitializationListener\".START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.MySessionBean3.START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.\"javax.servlet.jsp.jstl.tlv.PermittedTaglibsTLV\".START Missing[JBAS014861: ]",
        "jboss.deployment.unit.\"MyApp.war\".component.MySessionBean4.START Missing[JBAS014861: ]"
    ]},
    "rolled-back" => true
}
In this example, JBoss complains about every possible component of the web application, so it's kind of difficult to spot the real (and only) error in the deployment:
...
"jboss.naming.context.java.module.\"MyApp\".\"MyApp\".env.jms.MyConnectionFactory Missing[jboss.naming.context.java.jboss.resources.jms.MyConnectionFactory]",
        "jboss.web.deployment.default-host./myapp Missing[JBAS014861: ]",
...
The error was caused by the previously missing jndi-name element which was not needed when deploying the same application to AS6:
...
    java:/jms/MyConnectionFactory
...
After adding the JNDI name to the configuration, the deployment worked fine on AS7.
Note taken.

Java Alternatives on Ubuntu

This post shows how to install alternatives for all your JDK binaries in Ubuntu. Assume you have JDK 1.6.0_37 installed which contains the following binaries:
$ find /usr/lib/jvm/jdk1.6.0_37/bin -type f -executable | sort
/usr/lib/jvm/jdk1.6.0_37/bin/appletviewer
/usr/lib/jvm/jdk1.6.0_37/bin/apt
/usr/lib/jvm/jdk1.6.0_37/bin/ControlPanel
/usr/lib/jvm/jdk1.6.0_37/bin/extcheck
/usr/lib/jvm/jdk1.6.0_37/bin/HtmlConverter
/usr/lib/jvm/jdk1.6.0_37/bin/idlj
/usr/lib/jvm/jdk1.6.0_37/bin/jar
/usr/lib/jvm/jdk1.6.0_37/bin/jarsigner
/usr/lib/jvm/jdk1.6.0_37/bin/java
/usr/lib/jvm/jdk1.6.0_37/bin/javac
/usr/lib/jvm/jdk1.6.0_37/bin/javadoc
/usr/lib/jvm/jdk1.6.0_37/bin/javah
/usr/lib/jvm/jdk1.6.0_37/bin/javap
/usr/lib/jvm/jdk1.6.0_37/bin/javaws
/usr/lib/jvm/jdk1.6.0_37/bin/jconsole
/usr/lib/jvm/jdk1.6.0_37/bin/jcontrol
/usr/lib/jvm/jdk1.6.0_37/bin/jdb
/usr/lib/jvm/jdk1.6.0_37/bin/jhat
/usr/lib/jvm/jdk1.6.0_37/bin/jinfo
/usr/lib/jvm/jdk1.6.0_37/bin/jmap
/usr/lib/jvm/jdk1.6.0_37/bin/jps
/usr/lib/jvm/jdk1.6.0_37/bin/jrunscript
/usr/lib/jvm/jdk1.6.0_37/bin/jsadebugd
/usr/lib/jvm/jdk1.6.0_37/bin/jstack
/usr/lib/jvm/jdk1.6.0_37/bin/jstat
/usr/lib/jvm/jdk1.6.0_37/bin/jstatd
/usr/lib/jvm/jdk1.6.0_37/bin/jvisualvm
/usr/lib/jvm/jdk1.6.0_37/bin/keytool
/usr/lib/jvm/jdk1.6.0_37/bin/native2ascii
/usr/lib/jvm/jdk1.6.0_37/bin/orbd
/usr/lib/jvm/jdk1.6.0_37/bin/pack200
/usr/lib/jvm/jdk1.6.0_37/bin/policytool
/usr/lib/jvm/jdk1.6.0_37/bin/rmic
/usr/lib/jvm/jdk1.6.0_37/bin/rmid
/usr/lib/jvm/jdk1.6.0_37/bin/rmiregistry
/usr/lib/jvm/jdk1.6.0_37/bin/schemagen
/usr/lib/jvm/jdk1.6.0_37/bin/serialver
/usr/lib/jvm/jdk1.6.0_37/bin/servertool
/usr/lib/jvm/jdk1.6.0_37/bin/tnameserv
/usr/lib/jvm/jdk1.6.0_37/bin/unpack200
/usr/lib/jvm/jdk1.6.0_37/bin/wsgen
/usr/lib/jvm/jdk1.6.0_37/bin/wsimport
/usr/lib/jvm/jdk1.6.0_37/bin/xjc
You can do it manually for some of the important executables like java and javac:
sudo su
update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jdk1.6.0_37/bin/java" 3000
update-alternatives --install "/usr/bin/javac" "javac" "/usr/lib/jvm/jdk1.6.0_37/bin/javac" 3000
...
...but there are a lot of necessary binaries, so you can just use the command below to create alternatives for all of the executables in one step:
for f in `find /usr/lib/jvm/jdk1.6.0_37/bin/ -executable -type f | sort`; do update-alternatives --install "/usr/bin/${f##*/}" "${f##*/}" "$f" 3000; done
Or if you just want to have the commands for copy-paste etc.:
for f in `find /usr/lib/jvm/jdk1.6.0_37/bin/ -executable -type f | sort`; do echo "update-alternatives --install \"/usr/bin/${f##*/}\" \"${f##*/}\" \"$f\" 3000"; done
Regards.

2012. november 12., hétfő

Ubuntu 12.10 on Dell XPS 15z

Situation: Ubuntu 12.10 (including the live CD installer) hangs on startup on a Dell XPS 15z laptop. This may be the case for some earlier versions, and some other distros and laptops as well.

Finally I've found the kernel parameter that is needed specifically for Ubuntu 12.10 and the Dell XPS 15z laptop.

In the vast ocean of posts covering this and similar topics, the ones below were useful in the end. Thanks.

So the parameter is not acpi=noirq (still hangs) or acpi=off (overkill), but:

pci=noacpi

IBM MQ object authorization for JMS clients

Assume you have a queue manager named QMA, a queue named QUEUE1, and an appropriate channel defined in your IBM WebSphere MQ instance with proper authorization records for group "mqclient", e.g. you did something like:

setmqaut -m QMA -t qmgr -g mqclient +connect
setmqaut -m QMA -n QUEUE1 -t q -g mqclient +get +put +browse

Now, if you can successfully connect to the queue manager and the queue from a non-JMS IBM MQ client, e.g. the sample get client application:

$ /opt/mqm/samp/bin/amqsgetc QUEUE1 QMA
Sample AMQSGET0 start
no more messages
Sample AMQSGET0 end
$

...but not from a Java JMS client using the WebSphere MQ client libraries, because you get a "MQRC_NOT_AUTHORIZED (2035)" error, then you might end up wondering what's wrong with your client. The answer is in the "MQRC_NOT_AUTHORIZED (2035) for Java client accessing server" technote at IBM:

Cause
The WebSphere MQ Java classes inquire on some of the queue manager attributes while connecting.
Resolving the problem
Give the user +inquire authority to the queue manager object.
Example:
setmqaut -m QMGR -t qmgr -p user1 +connect
So in our case:
setmqaut -m QMA -t qmgr -g mqclient +connect +inq
setmqaut -m QMA -n QUEUE1 -t q -g mqclient +get +put +browse +inq

Bottom line: if you plan to access your MQ objects from JMS clients, give also +inquire authority to your clients. And yes, as the second line shows, this is needed not only for queue managers but for queues as well, independent of whether you are using QueueBrowsers or MessageConsumers.

Note: Don't forget to REFRESH SECURITY in MQSC or restart your queue manager QMA after this.

Problems with update() Operation in Human Task Data Controls

Suppose you have an ADF 11.1.1.5.0 UI implementing a BPM human task, i.e. a bounded task flow using the generated human task data controls (based on the task definition), and your specific BTF implementation with your pages. To update attributes in the BPM payload, you can use the update() operation of the HT data control. This is working fine on the auto-generated task form and on any other JSF page.

In ADF task flows, any kind of activity can have bindings/pageDefs defined, which means that method call or data control operation activities in your BTF can be used to access your custom BPM payload or any other BPM metadata coming from the getTaskDetails() operation in the data control.

The sad part comes as soon as you try to call the update() operation directly from the BTF, and not from a JSF view (e.g. in a button action). For example, you can do that in a method call activity which has an associated binding container that contains all the necessary bindings for getTaskDetails(), its results, and the update() operation inside it. But then you realize that it is not working, and throws the following NullPointerException:

<Apr 11, 2012 11:41:48 AM CEST> <Error> <oracle.adf.model.adapter>
<BEA-000000> <Exception invoking method from XML data control.
Cause:java.lang.NullPointerException:
java.lang.NullPointerException
       at oracle.bpel.services.workflow.datacontrol.WorkflowService.saveHistory(WorkflowService.java:831)
       at oracle.bpel.services.workflow.datacontrol.WorkflowService.invokeWorkflowDCMethods(WorkflowService.java:236)
       at oracle.bpel.services.datacontrol.XSDDataControl.invokeWorkflowOperation(XSDDataControl.java:373)
       at oracle.bpel.services.datacontrol.XSDDataControl.invokeOperation(XSDDataControl.java:467)
       at oracle.adf.model.bean.DCBeanDataControl.invokeMethod(DCBeanDataControl.java:430)
       at oracle.adf.model.binding.DCInvokeMethod.callMethod(DCInvokeMethod.java:261)
       at oracle.jbo.uicli.binding.JUCtrlActionBinding.doIt(JUCtrlActionBinding.java:1635)
       at oracle.adf.model.binding.DCDataControl.invokeOperation(DCDataControl.java:2149)
       at oracle.adf.model.bean.DCBeanDataControl.invokeOperation(DCBeanDataControl.java:467)
       at oracle.adf.model.adapter.AdapterDCService.invokeOperation(AdapterDCService.java:307)
       at oracle.jbo.uicli.binding.JUCtrlActionBinding.invoke(JUCtrlActionBinding.java:740)
       at oracle.adf.controller.v2.lifecycle.PageLifecycleImpl.executeEvent(PageLifecycleImpl.java:402)
       at oracle.adfinternal.view.faces.model.binding.FacesCtrlActionBinding._execute(FacesCtrlActionBinding.java:252)
       at oracle.adfinternal.view.faces.model.binding.FacesCtrlActionBinding.execute(FacesCtrlActionBinding.java:210)
       at mypackage.MyBean.doSomethingAndUpdate(MyBean.java:666)
       ...

After hours of investigation and trial and error of no avail, and without having the sources of the XSDDataControl and WorkflowService classes (which can be found in bpm-workflow-datacontrol.jar), one can only guess why this is happening. (Which, to be honest, is mostly the case when developing human task interfaces, thanks to the poor documentation on human task form development.)

Again, it works when called from a view, but fails with a NPE when called from a method call activity in an ADF task flow, i.e. outside a view, where you do have a FacesContext, but not a UIViewRoot as you have not yet entered any view activity. Is it possible that the WorkflowService.saveHistory() method tries to access the JSF view root? Let's give it a try, and create a fake view before calling update():

// in the method call activity
String someFakeViewId = ...;
FacesContext fctx = FacesContext.getCurrentInstance();
Application application = fctx.getApplication();
ViewHandler viewHandler = application.getViewHandler();
UIViewRoot viewRoot = viewHandler.createView(fctx, someFakeViewId);
fctx.setViewRoot(viewRoot);
// update() after this

If you try this, you can see that it's working now. So we can (vaguely) guess that some call is being made on FacesContext.getViewRoot() in WorkflowService.saveHistory(), which of course does not makes much sense (i.e. calling back to the view layer from a data control).

Until this gets fixed, you can use this as a workaround. Or just avoid using method call activities for BPM payload updates... ;)