A few days back, I asked IBM to give me my 3 days back. Obviously, that's not happening. So here is my contribution to the community so others don't have to loose time like I did. You can create a BUS and an outbound service as described in the series of articles at http://www-128.ibm.com/developerworks/websphere/techjournal/0512_reinitz/0512_reinitz.html. The link points to the last part of the article, which contains links to other parts.
Let's understand creating outbound service in little more detail. In WebSphere 6.0, creating an outbound service
Automatically creates a record in the SDO repository database
The record consists of your wsdl file inserted into the database as a blob
The record uses a string in dest:<bus_name>:<service_name> format as the primary key. So, for an outbound service destination named MyService in bus MyBus, the wsdl is inserted using dest:MyBus:MyService as the key
When you forward the message from queue to the outbound service, you must use a mediation to make WAS messaging engine aware that this is a soap message by applying a so called 'format string'
The format string must be of the format SOAP:dest:[busname]:[service destination name],[service namespace],[service name],[port name]
Out of this format string, WAS uses dest:[busname]:[service destination name] part as the key to retrieve the wsdl from the database
In WebSphere 6.1, everything else remained the same BUT format string and the key used to insert the wsdl in the database changed. In WebSphere 6.1, creating an outbound service
Automatically creates a record in the SDO repository database
The record consists of your wsdl file inserted into the database as a blob
When you forward the message from queue to the outbound service, you must use a mediation to make WAS messaging engine aware that this is a soap message by applying a so called 'format string'
The format string must be of the format SOAP:<wsdlLocation>,<serviceNameSpace>,<serviceName>,<portName>
Out of this format string, WAS uses <wsdlLocation> part as the key to retrieve the wsdl from the database
If you use WAS 6.0 format string in WAS 6.1, you will get a validation error. The bug in WAS 6.1 is, when you create an outbound service, WAS 6.1 still uses the old format of key to insert the record BUT forces you to use the new format to retrieve the record so you will NEVER get any results back. Since WAS 6.1 uses entity beans to access SDO Repository, you will get ObjectNotFoundException.
Start the wsadmin tool by going to <profile_home>/bin and issuing wsadmin command (wsadmin.sh on linux/unix). If you are on a secure server, you will have to use -user and -password flags to use admin commands in wsadmin tool
Use the following commands to list the resources in your SDO repository
wsadmin> set sdoRep [$AdminControl queryNames *,type=SdoRepository,node=[$AdminControl getNode]] this command defines a variable called sdoRep
wsadmin> puts [$AdminControl invoke $sdoRep listResources] this command lists the keys by which xml files are stored as blobs in your SDO repository
If you have defined outbound service correctly, you will see an entry dest:<bus_name>:<service_name>. For example, if you had an outbound service named MyService in bus MyBus, you will see an entry dest:MyBus:MyService
Use "SOAP:dest:MyBus:MyService" as the format string in your mediation and you will get a validation error in ffdc log files. This is because WAS 6.1 forces you to use wsdl location
Use SOAP:<wsdlLocation>,<serviceNameSpace>,<serviceName>,<portName> as the key and you will get ObjectNotFoundException because there is no record with your wsdl location as the primary key
The workaround:
To work around this problem, you have to manually insert your wsdl in the SDO repository database using your wsdl location as the key. To do this,
Create a scrip file called sdoWsdlImport.jacl and put the following content in it:
# script to add wsdl to SDO repository
set xsdFile [lindex $argv 0]
set xsdKey[lindex $argv 1]
set sdoRep [$AdminControl queryNames *,type=SdoRepository,node=[$AdminControlgetNode]]
owsadmin -f invokes the wsadmin tool and runs the script
oc:/temp/sdoWsdlImport.jacl is the full path to the jacl file (notice forward slash in windows environment)
o pathC:/MySoftware/RAD7workspace/my_messaging_ejb/ejbModule/META-INF/wsdl/MyService.wsdl is the physical location of wsdl file. On the server, you can use location of wsdl in 'installedApps' directory. Again, notice forward slash
Now, start the wsadmin tool as described above and use the same commands to list the contents of the SDO repository
wsadmin> set sdoRep [$AdminControl queryNames *,type=SdoRepository,node=[$AdminControl getNode]] this command defines a variable called sdoRep
wsadmin> puts [$AdminControl invoke $sdoRep listResources] this command lists the keys by which xml files are stored as blobs in your SDO repository
This time you should see an entry with your wsdl location. This means there is a record in the SDO repository database with your wsdl location as the primary key
Now run your test code to forward the soap message form the queue to outbound service using format string in SOAP:<wsdlLocation>,<serviceNameSpace>,<serviceName>,<portName> format in your mediation and your web service should be invoked correctly
...Because that's how long it took me to find and fix a bug in the [not so] new WebSphere V6.1 product.
Let me present some background before I start to rant! Starting V6.0, WAS has a concept of Service Integration BUS (or SIBUS). You can use this to forward an xml/soap message from a receiving queue to web service. You can also put mediations on the request and return paths to inspect/manipulate the message. This eliminates the need to parse an incoming xml message while giving a lot of flexibility in other areas. Actually, you can do a lot more with SIBUS but let me just rant for today.
WAS represents the message in Service Data Object (SDO) format because it's much easier to work with objects when you are inspecting or manipulating the message or its path. You have to install an SDO repository - a database where your wsdl and other service information is stored so that WAS can create SDO object structure – for this to work. When you create an SIBUS web service using the admin console, WAS 6.1 'should' put the service information - especially wsdl file in the SDO database using wsdl location as the primary key. So, MyService.wsdl will be stored using http://localhost:<port>/<context_root>/services/MyService/wsdl/MyService.wsdl as the key. This happens automatically when you create the SIBUS service through admin console. When you use the service, you specify a so called format string to identify the wsdl (the key of the SDO repository) the incoming soap message belongs to. Specifying the format string will give you complete SDO structure including service name, parameters, header, attachment and body in object form so that you can manipulate anything you want. You can even change the message route!
So far so good. My problem started when I successfully defined the service but couldn't retrieve it as SDO. Surprisingly, no error was reported but message simply didn't forward to my web service from the receiving queue. Upon close inspection of log files in ffdc log directory, I got a message similar to 'wsdl could not be located.' And upon closer inspection (and of course a lot time wasting), I found an ObjectNotFoundException on SDO repository. Obviously, I was using wrong key (format string) to retrieve my data. Now, according to WAS 6.1 info center, format string should be defined in the following format:
SOAP:<wsdl_location><other_parameters>
And that is exactly what I was using. This led me to reinstalling SDO repository, recreating server profile, redeploying ear files and web services and a lot more not to mention, lost sleep and eating/drinking unhealthy stuff while desperately trying to figure out what was going on.
Finally, I decided to try WAS 6.0 supported format of format string:
SOAP:dest:<bus_name>,<other_parameters>
Correctly, I got an incorrect format string error because there was a validation component watching the format string I was passing. As it turns out while the validation component was watching the format string correctly, IBM WAS developers were NOT watching the key they used to create a record in the SDO repository for an SIBUS web service.
When you create a web service in SIBUS, WAS 6.1 uses WRONG KEY to insert the record in the repository. It uses "SOAP:dest…" format. So there is a record in the repository for your web service but you can never retrieve it because of the validation component! If you use the WAS 6.1 recommended format, "SOAP:<wsdl_location>…" you will never get any results back because the data wasn't inserted with that key in the first place. In other words, creating a web service within SIBUS will NOT work correctly in WAS 6.1.
It took me 3 days to figure it out. Finally, I ended up writing some jacl script to fix this issue (I will share the scripts in near future). What is ironic is, "SOAP:dest:…" format is not even mentioned in WAS 6.0 OR WAS 6.1 info center. I found this format from an article on developerworks at http://www-128.ibm.com/developerworks/websphere/techjournal/0509_reinitz/0509_reinitz.html. Info center always mentioned "SOAP:<wsdl_location>," which makes sense because every web service within a bus will have unique wsdl location so location should be used to store objects in SDO repository.
The lesson learned? If you are using WAS 6.0, use "SOAP:dest…" format to specify format string. And if you are using WAS 6.1, good luck using web services in SIBUS environment! Well... just use the jacl script I will share soon to change the SDO repository to make the correct format string work.
This blog entry is a rant - a big
one against spring, hibernate combo.
If you run a database query and do
not get any results back, here is what you would typically do:
Check logs etc. to make sure there is no exception
Check to make sure the data is available in the
database
Check to make sure the search criteria are being
passed correctly
Check to make sure the query you are issuing or
generating is correct
Check to make sure the code that receives the results
is correctly interpreting that the result set is in fact empty
If you haven't spent too much time debugging this
issue yet, it will probably occur to you to make sure you are searching
against the correct database schema
However, if you are using spring -
hibernate combo, let me give you one more
Check that all hibernate mapping
files are added to the list of mapping files in the mappingResources property
of LocalSessionFactoryBean provided by spring.
I didn't, and it cost me a long
day and night figuring it out. But that's no ranting, that's lesson learned.
The ranting comes from the fact that neither spring nor hibernate cared to tell
me about a missing mapping file. If my DetachedCriteria is defined for
MyDomain, I would expect hibernate to lookup its mapping file and tell me if it
can't find the file. Instead, hibernate ran the query happily and returned zero
results! No errors, no exceptions, no warning - not even a tiny debug
statement. In fact, the behavior of the framework was misleading. Usually, when
you set show_sql to true, hibernate prints all the sql queries it generates. In
this case, it didn't print the query leading me to believe it couldn't generate
the query. I first blamed it on my usage of projections, then on result set
transformer, then on hibernate filters, then on the version of business objects
jar file I was using, then on WebSphere, then on database, then on hibernate
caching and the list goes on... For every possibility, I ended up changing
code/configuration, adding more log statements, redeploying the application and
testing. I even ran another transaction that used the same DAO method and made
sure that query was printing in the log and executing correctly.
Many will put the blame on me and
they are right if they do. If the framework says you have to specify mapping
files, you have to or face consequences, but I was just hoping to see the
consequences in my log files.
The story doesn't end here. After
I finally added the mapping file entry and ran again, I got an error similar to
"no class MySubDomain found." This is valid because MyDomain would
return results as MySubDomain based on a column selector BUT I think the error
is misleading. In a different situation, the developer would think the class
was not packaged in the domain objects jar file. In my case however, since I
knew my earlier problem was a missing mapping file, it immediately occurred to
me that the mapping file for MySubDomain was probably missing. After adding
both mapping files, hibernate executed the query (like it did before) but this
time it also returned results (thank you!).
This incident has created yet
another dent in my love for spring - hibernate as enterprise frameworks
(yes, there are some dents already but let's talk about them later!). As of
now, I don't know whether spring or hibernate caused this. I will try to find
out when I am done with other more important things I have to do. In the mean
time, my ranting continues...
I
recently received an email through my website about what is the future of
certain technologies and how one should move forward especially if he/she has
lost touch with some of the new technologies. I thought the email and my
response to it might be useful to the community so I am posting them here as they
are (without any changes)...
<email>
I
was pretty impressed by your thoughts. I think your ideas and presentaion of
problems was real good. I found your blog by accident on searching for my
destination which direction move about the technologies in J2EE with
EJB/Websphere/RAD/WSAD/DB2 and wondering about spring/hibernate/EJB3 how I can
adopt them within our infrastructure. Pretty much confused. This maily because
I guess I lost touch with new techs, whatz happening there. Recently came
across SOA forum meeting and found it was mazing. At the same time felt very
in-secured about my self. I am sorry for writng things with no clear picture
but this how I am going through today. Hope I would find some light and
direction from you and your site in future.
</email>
<my
response>
Thank
you for your note. While I will continue to post on my website (unfortunately,
it doesn't happen as frequently as I would like) let me respond to your email
as well.
First,
it's not your fault that you feel insecure or are confused about the direction
of your career and the direction of technology market as a whole. Technology
keeps changing fast. New frameworks, specifications and tools keep coming out
all the time. If for some reason, you don't use them, you feel like being in a
whole different world in a pretty short period of time.
This
has happened to me. I was on an analysis/architecture project for some time.
While that experience was very valuable, when I finished, there were Hibernate,
JSF I had absolutely no idea about. Additionally, technology companies
intentionally keep churning out new products and programming models frequently
to continue to make consulting money.
Here
are some pointers that might help you move forward
1.
Try to have a fairly good understanding of tools from at least one vendor. For
example, I specialize in IBM offerings: WebSphere, RAD, DB2, Portal, Content
management etc. I don't know JBoss and WebLogic as well but that has never been
a problem. Knowing just Java/J2EE is not enough. You must know tools. Recently,
we lost a developer because he couldn't even successfully import projects in
WSAD/RAD. He was good in writing code but we had to spend a lot of time holding
his hands, which was quite ineffective for the whole team
2.
Never forget to think of a problem in terms of architecture and design. I
always ask my developers to think in terms of business problem -->
architecture --> design --> code. This becomes more and more important as
you transition from programmer to developer to designer to architect and so on
3.
As far as technologies and framework go, focus more on J2EE than on Spring and
Hibernate. There are plenty of applications deployed on J2EE and they are
likely to go to Java EE 5 and EJB 3 than to Spring/Hibernate. Why? Because they
will be forced to migrate when they migrate to newer versions of the
application servers they are using. For example, pretty soon, WebSphere 5.x
will not be supported so companies will be forced to upgrade from J2EE 1.3. And
it's easy to go from J2EE 1.3 to 1.4 or 5 but not to Spring/Hibernate. Even for
new applications, I see Spring/Hibernate being used more in conjunction with
J2EE than standalone. It takes a really good architect to create a pure
Spring/Hibernate/POJO based enterprise application - very few of them exist and
they are not available. Knowing Struts is a must though. It's an old framework;
pretty much used everywhere - old and new projects alike. I don't see JSF
replacing struts any time soon because of lack of available JSF skills
4.
SOA and web services - definitely the way to go in the future. As you may have
seen on my website survey, that is what people think as well! Why are SOA and
web services important? They solve the next level of complexity in IT. Remember
when HTML was hot and HTML programmers were paid a lot of money? Now, you can
create an html page in Microsoft word (creates ugly code though)! Remember when
if you knew C language, you were considered smart (and geek)? IT industry wants
to solve more complex problems and in the process, it invents the next level.
Lower level languages and technologies, while important, don't remain lucrative
forever. Java will probably face the same problem (not in the near future
though) in the sense that writing Java programs will get easier and easier
requiring lower level skills. But, creating SOA/web services based systems will
require advanced skills and knowledge of the latest tools and specifications.
If you have these skills, you will bring more value to the table.
To
summarize,
1.
Focus on at least one tool
2.
Always know that business problem drives the IT solution 2. Stay with the
specifications (J2EE) 3. Don't ignore SOA and web services
Using spring and EJB togetherÂ… and violating EJB 2.x specification
Written by Chintan Rajyaguru
Thursday, 21 September 2006 17:20
How to use Spring and EJB together
It is well documented that you can
use spring framework and EJBs in the same application. Let me quickly outline
how this is done:
As you can see in the figure
above,
ALL the business methods are defined in a so called
business interface, which is a Plain Old Java Interface (POJI). This
interface is extended by both remote and local interfaces of the EJB and
hence the bean gets the business methods as EJB methods
The bean also implements the business interface and
is forced to define implementation of all the business methods. Within the
implementation of the business methods, the bean class simply delegates
the method call to MyPOJO
MyPOJO is a java class, which implements the business
interface and provides the implementations of the business methods. This
allows for easy testing of business methods by invoking POJO outside the
container
The bean class maintains an instance variable of type
MyBizInterface. The underlying implementation (POJO) of this interface is
provided by spring. When a business method is invoked on the bean class,
the bean class delegates the method call to the POJO. Like this:
//
AbstractStatelessSessionBean is spring framework provided bean
// this method must be implemented because
the bean class implements the
// business interface
public void bizMethod (){
// delegate to POJO
myService.bizMethod();
}
}
The problems
When you add a business method,
you have to add it to both bean class and the POJO. There are two problems with
this approach as I see it:
The bean class doesn't really need to implement the
business interface. The only purpose implementing the business interface
serves is that it forces the bean class to implement the business method.
This doesn't accomplish anything because you would have implemented all
business methods (and delegated to POJO) in the bean class anyway
According to EJB specification, the business methods
defined in the remote interface must throw
java.rmi.RemoteException. The specification also states that the business
methods defined in the local interface must NOT throw
RemoteException. Since we use one business interface to define methods for
both local and remote interfaces, we cannot meet both the requirements.
Currently (in WebSphere), if you declare methods in the local interface to
throw RemoteException, you only get warning but it's still violation of
the specification
No problem when using EJB 3.0
specification
Per section 3.3 of simplified EJB
specification, "...the methods of the business interface should not
throw the java.rmi.RemoteException, even if the interface is a remote business
interface..." In this case, the business interface will simply need to
define the business methods (without any exceptions) and remote and local
interface need to extend the business interface.