Generating a JAX-WS Client JAR From WSDL URL Using PowerShell
Don't underestimate the power of PowerShell.
Join the DZone community and get the full member experience.
Join For FreeSometimes, I like to get to the point directly rather than read an entire article. So if you are short on time, you may want to skip to the final step where you just have to copy the code, save it as a PowerShell script file, and run it with PowerShell.
However, if you are in the mood to do a little reading, then let's begin.
You may also like: [DZone Refcard] Windows PowerShell
1) Removing Unnecessary Files and Folders for a Clean Start
We will be dealing with WSDL, XSD, Jar, and XML files as well as some folders. To prevent any mistakes, let's first get rid of all the files with these extensions and the folders we will be working with.
Get-ChildItem
returns the child items in the path specified. To get all the child items in subfolders, we use the Recurse
parameter. The returned items can then be deleted with the Remove-Item
command.
Get-ChildItem -Path "./*.wsdl" -Recurse | Remove-Item -force -recurse
Get-ChildItem -Path "./*.xsd" -Recurse | Remove-Item -force -recurse
Get-ChildItem -Path "./*.jar" -Recurse | Remove-Item -force -recurse
Remove-Item -LiteralPath "./files" -Force -Recurse -ErrorAction SilentlyContinue
2) Creating Necessary Folders
We need our target
and META-INF
folders under the files
folder. We can then create them with a new-item
command.
new-item -Name "./files" -ItemType directory
new-item -Name "./files/target" -ItemType directory
new-item -Name "./files/META-INF" -ItemType directory
3) Creating Necessary Files
We will be using JAXB for generating Java classes from XML. Let's now create a jaxWs
catalog and jaxbBindings
files with their content embedded in the script.
$jaxWsCatalog = '<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"><system systemId="http://localhost/wsdl/service.wsdl"uri="service.wsdl"/></catalog>'
$jaxbBindings = '<?xml version="1.0" encoding="UTF-8"?><jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:version="2.1"><jaxb:globalBindings><xjc:serializable uid="1"/></jaxb:globalBindings></jaxb:bindings>'
$jaxWsCatalog | Set-Content "files/jax-ws-catalog.xml"
$jaxbBindings | Set-Content "files/Jaxb-bindings.xml"
4) Requesting the WSDL URL From the User and Retrieving Wsdl Content
All of the files and folders are ready. We now need to ask the user for the WSDL URL. In PowerShell, you can get an input from the user with Read-Host
. Once we get the URL, we can get the content of the wsdl with the Invoke-WebRequest
command.
$wsdlUrl = Read-Host 'Please enter wsdl url'
$WsdlOriginal = (Invoke-WebRequest $wsdlUrl).Content
5) Checking for XSD Existence
The WSDL content may contain all xsd file information. We will check its existence by checking if the content contains schemaLocation
or not using the IndexOf
command.
$startIndex = [int]$WsdlOriginal.IndexOf("schemaLocation")+16
if( $startIndex -ge 16)
If XSD does not exist (the index is less then 16), we must skip to step 8. If it exists, then we have to retrieve the xsd file and modify the WSDL content retrieved in step 4.
6) Retrieving the XSD Content and Writing it to a File
By using the OutFile
parameter, we can write the content retrieved with Invoke-WebRequest
to a file with a name that we will give. In our example, it is service.xsd
:
Invoke-WebRequest -OutFile service.xsd $xsdUrl
7) Modifying WSDL Content
We retrieved the XSD file defined in the WSDL content. We need to change the reference of the XSD file from our URL to the local file. The start index of the string to be replaced was found in step 5. The end index is the position of the first quote symbol after the start index. This index can be found with:
$endIndex = [int]$WsdlOriginal.IndexOf('"',$startIndex)
The string between the start and end index must be replaced with the name of the XSD file ( service.xsd
). This can ben achieved with the join
command.
$WsdlModified = -join($WsdlOriginal.Substring(0,$startIndex),"service.xsd",$WsdlOriginal.Substring($endIndex))
The second modification we need to make is to replace the IP of the service-port-address-location with localhost
.
$startIndex = [int]$WsdlModified.IndexOf('address location="http://')+25
$endIndex = [int]$WsdlModified.IndexOf(':',$startIndex)
$WsdlModified = -join($WsdlModified.Substring(0,$startIndex),"localhost",$WsdlModified.Substring($endIndex))
8) Writing WSDL content to a File
Our WSDL content ( $WsdlOriginal
if we skipped steps 6 and 7, $WsdlModified
otherwise) is ready. Let's now write it to a file with the Set-Content
command.
$WsdlModified | Set-Content "service.wsdl"
9) Retrieving a Package Name From the User
Let's get the full package name of the classes to be generated from the user.
$packageName = Read-Host 'Please enter package name'
10) Copying the Necessary Files
The files we generated in earlier steps must be in the folders we created in step 2. Let's copy the files to the folders:
copy service.wsdl files\
copy *.xsd files\
cd files
copy service.wsdl META-INF\
copy *.xsd META-INF\
copy jax-ws-catalog.xml META-INF\
11) Generating the Client
The preparation is complete. Let's create our client into the target
folder with the package name $packageName
using the Jaxb-bindings.xml
file and XautoNameResolution
parameter to prevent a problem with the class name already in use.
wsimport -d target -keep -p $packageName -b Jaxb-bindings.xml -B-XautoNameResolution -wsdllocation http://localhost/wsdl/service.wsdl -catalog jax-ws-catalog.xml service.wsdl
12) Removing Java Files
Our client has been generated. We have got the Java files and the class files. To decrease the size of the final JAR, let's get rid of the Java files. You may skip this step if you want to keep the Java files.
$packageName = [string]$packageName -replace "\.", "/"
Get-ChildItem -Path $packageName *.java | foreach { Remove-Item -Path $_.FullName }
13) Creating a JAR File and Moving it to the Root Folder
Now, let's create the JAR file and move it to the root folder.
jar cvf service.jar .
cd ..
cd ..
Move-Item files/target/service.jar ./
14) Final Code
If we combine all the steps above and add some controls and comments, we will get the result below. Once you save the following code in the PowerShell script file and run it, all you have to do to generate a JAX-WS client JAR and enter a valid WSDL URL and enter the package name for the generated classes.
#Removal of unnecessary files and folders for a clean start
Get-ChildItem -Path "./*.wsdl" -Recurse | Remove-Item -force -recurse
Get-ChildItem -Path "./*.xsd" -Recurse | Remove-Item -force -recurse
Get-ChildItem -Path "./*.jar" -Recurse | Remove-Item -force -recurse
Remove-Item -LiteralPath "./files" -Force -Recurse -ErrorAction SilentlyContinue
#Creation of necessary folders
new-item -Name "./files" -ItemType directory
new-item -Name "./files/target" -ItemType directory
new-item -Name "./files/META-INF" -ItemType directory
#Creation of necessary files
$jaxWsCatalog = '<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"><system systemId="http://localhost/wsdl/service.wsdl"uri="service.wsdl"/></catalog>'
$jaxbBindings = '<?xml version="1.0" encoding="UTF-8"?><jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:version="2.1"><jaxb:globalBindings><xjc:serializable uid="1"/></jaxb:globalBindings></jaxb:bindings>'
$jaxWsCatalog | Set-Content "files/jax-ws-catalog.xml"
$jaxbBindings | Set-Content "files/Jaxb-bindings.xml"
Write-Output ''
Write-Output 'Deleted unnecessary files/folders'
Write-Output 'Created necessary files/folders'
Write-Output ''
#Request wsdl url from user and retrieve wsdl content
$wsdlUrl = Read-Host 'Please enter wsdl url'
$WsdlOriginal = (Invoke-WebRequest $wsdlUrl).Content
Try
{
#Check if the wsdl contains xsd file definition by looking for schemaLocation tag name
$startIndex = [int]$WsdlOriginal.IndexOf("schemaLocation")+16
#if xsd exists
if( $startIndex -ge 16)
{
$endIndex = [int]$WsdlOriginal.IndexOf('"',$startIndex)
Try{
#retrieve the xsd url from wsdl
$xsdUrl = [string]$WsdlOriginal.Substring($startIndex,$endIndex-$startIndex)
#if the xsd url is not a full path url, append the prefix of the wsdl url to the begining of xsd url
if( ([int]$xsdUrl.IndexOf("http")) -lt 0 )
{
$xsdUrl = -join($wsdlUrl.Substring(0,($wsdlUrl.LastIndexOf('/')+1)),$xsdUrl)
}
#retrieve the xsd file from the xsd url and then write its content to service.xsd file
Invoke-WebRequest -OutFile service.xsd $xsdUrl
}Catch
{
#if something goes wrong, ask the user for the valid xsd url and then retriev and write its content to service.xsd file
$xsdUrl = Read-Host 'Please enter valid xsd url'
Invoke-WebRequest -OutFile service.xsd $xsdUrl
}
#replace the xsd schemaLocation value with the file name of the xsd
$WsdlModified = -join($WsdlOriginal.Substring(0,$startIndex),"service.xsd",$WsdlOriginal.Substring($endIndex))
#replace the ip of the service-port-address-location with localhost
$startIndex = [int]$WsdlModified.IndexOf('address location="http://')+25
if( $startIndex -ge 0 )
{
$endIndex = [int]$WsdlModified.IndexOf(':',$startIndex)
$WsdlModified = -join($WsdlModified.Substring(0,$startIndex),"localhost",$WsdlModified.Substring($endIndex))
}else
{
Write-Output 'ATTENTION! service port address ip could not be replaced with localhost. Change must be made manually!'
}
#write the modified wsdl to service.wsdl file
$WsdlModified | Set-Content "service.wsdl"
}else
{
#There is no xsd. For this reason no need to make changes with wsdl so write it to service.wsdl as it is.
$WsdlOriginal | Set-Content "service.wsdl"
}
Write-Output 'Check input files and make necessary changes if needed'
pause
#ask user for package name
$packageName = Read-Host 'Please enter package name'
#copy necessary files to folders
copy service.wsdl files\
copy *.xsd files\
cd files
copy service.wsdl META-INF\
copy *.xsd META-INF\
copy jax-ws-catalog.xml META-INF\
#generate client
wsimport -d target -keep -p $packageName -b Jaxb-bindings.xml -B-XautoNameResolution -wsdllocation http://localhost/wsdl/service.wsdl -catalog jax-ws-catalog.xml service.wsdl
#remove .java files to minimize the size of the client jar
cd target
$packageName = [string]$packageName -replace "\.", "/"
Get-ChildItem -Path $packageName *.java | foreach { Remove-Item -Path $_.FullName }
#create the jar file and move it to the same folder with the powershell script file
jar cvf service.jar .
cd ..
cd ..
Move-Item files/target/service.jar ./
pause
}Catch
{
Write-Output "An error occured. Open the powershell script file and check each step"
}
That's it! Happy coding!
Further Reading
[DZone Refcard] Windows PowerShell
Opinions expressed by DZone contributors are their own.
Comments