Dustin's Software Development Cogitations and Speculations My observations and thoughts concerning software development (general development, Java, JavaFX, Groovy, Flex, ...). Select posts from this blog are syndicated on DZone and Java Code Geeks and were formerly syndicated on JavaWorld.
Monday, February 25, 2008
Flex 3 Released Today
One impact the timing of this release of Flex 3 has on me is that I now need to update my slides for Collaborate08 regarding Flex 2 and Flex 3.
Saturday, February 23, 2008
Windows PowerShell and Java
Many Java applications require the environment variable
JAVA_HOME
to be set appropriately to work correctly. In PowerShell, you can check the setting of a particular environment variable with the command echo $env:JAVA_HOME
. An example of this is shown in the next screen snapshot (click on image to see larger version).Another environment variable that is often useful to note when using Java is any CLASSPATH setting. This can be checked with the
echo $env:CLASSPATH
command. If the CLASSPATH environment variable is set, it will be displayed. What if you wanted to set your classpath for use in Java applications rather than passing the classpath to the application with the Java launcher? In that case, you could set CLASSPATH with the command $env:CLASSPATH = "<<someClassPath>>"
.The quotation marks are significant in setting the classpath because the semicolon (
;
) is still used to separate class path entries. Because the semicolon also tells PowerShell that a command is being terminated and another started, this must be in quotation marks to avoid the classpath being treated as two separate statements.The classpath can also be set on the command-line in conjunction with the Java launcher with a command like:
java -cp "<<someClassPathEntry>>;<<anotherClassPathEntry>>" <<package>>.<<classToRun>>
(again, as with setting the CLASSPATH environment variable, quotations should be used around the entire classpath entry so that the semicolon is not treated as a statement terminator). A concrete example of this is shown next:
java -cp "C:\NetBeansProjects\SpringJmxExample\build\classes;C:\spring-framework-2.5.1\dist\spring.jar;C:\spring-framework-2.5.1\lib\jakarta-commons\commons-logging.jar" marx.SpringJmxMain
The take-aways from all of this include the following:
- Although many Unix-isms can be used in PowerShell, one should still use the semicolon for classpath separators as described in the Windows Setting the Class Path document rather than with colons for Unix as documented in the Solaris Setting the Class Path document.
- PowerShell does recognize semicolons as statement terminators for placing multiple statements on the same line. This very Unix-like feature means that class path values must be surrounded in quotes so that multiple entries can be separated with semicolons without making PowerShell think there are multiple statements on the line.
- Although PowerShell "feels" much more like Unix than DOS, it still supports DOS operations and the semicolon and backwards slashes in the examples above shown that this is the same as in DOS as well. That being said, I can also use more Unix-like syntax and have it work as well. The command shown above could also be run as follows:
java -cp "/NetBeansProjects/SpringJmxExample/build/classes;/spring-framework-2.5.1/dist/spring.jar;/spring-framework-2.5.1/lib/jakarta-commons/commons-logging.jar"
marx.SpringJmxMain
The above command will behave exactly as the one shown above, but without the DOS-obviousC:\
drive syntax and the backwards slashes. - It is not shown above, but you can find out more about using PowerShell environment variables by running the command
man env
(orhelp env
) in the PowerShell terminal window.
When specifying Java system properties on the command line in PowerShell, you need to put quotation marks around the these specifications so that the
-D
is not interpreted prematurely. The next screen snapshot shows running Java with the -D
system properties options without quotes and the error that follows. The same screen snapshot then shows how it runs successfully if double quotes are used around the -D
system properties.You can run the
java -version
command in PowerShell just as you would in DOS or Unix/Linux. The next screen snapshot demonstrates this.Similarly, you can run other Java executable tools in the same way you'd run them in DOS or in Unix/Linux. The next screen snapshot shows the command-line view of running these in PowerShell.
PowerShell brings each of us a more "natural" Java development command-line experience if we've developed Java primarily in a Unix/Linux environment in the past.
It is not as easy as I would have expected to find good blogs and articles on using Java with PowerShell. This may be due somewhat to the chasm that often seems to exist between the Windows community and Java or other non-.NET communities. Here are some interesting links related to using Java with PowerShell:
Hello Windows PowerShell
One of the things that is nice about PowerShell is that is supports many of the commands that we like to use in Unix/Linux. There are far too many useful things to demonstrate about PowerShell in one place, but the next two screenshots (click on them to see larger versions) demonstrate some Unix-like commands supported in Windows PowerShell.
The first screen snapshot demonstrates the very handy
history
command along with the ability to run a man
(equivalent to help
) command for a particular command.The next screen snapshot shows the
ls
command along with the mv
command. Having Unix equivalents makes it less frustrating to switch between Unix/Linux and Windows. Also, the ls
and mkdir
commands demonstrate the displaying of file modes.Besides the Unix/Linux commands shown in the screen snapshots above, other goodies that are now available in PowerShell include
pwd
, ps
, and cat
. It is amazing how nice it is to have these little "extras" in the DOS scripting world.Windows PowerShell requires .NET framework 2.0, so you'll need to get this if you don't already have it. There is much, much more to Windows PowerShell than what I've shown here so far. I think it is likely that I'll post future blog entries on other great features of PowerShell. It is even more likely that I'll start using PowerShell more frequently in my blog entries that show me running scripts, Java commands, the Flex compiler, etc. from the command line rather than using the old-style DOS terminal.
In related resources, Vaibhav discusses running PowerShell from Java. An interesting related article is A Return to Command-Line Control with Windows PowerShell. A free Windows PowerShell eBook is available as well. Finally, two other introductory references for using PowerShell are What is Windows PowerShell? and Top Ten Tips for Using Windows PowerShell Finally, a gentle introduction to Windows PowerShell is available in Discover PowerShell.
Monday, February 18, 2008
Use Trang to Generate XML Schema
Besides being useful for converting between XML schema definitions, Trang is also useful for generating a new XML schema definition from one or more source XML files. In fact, this use of Trang is the focus of this blog entry. In this blog entry, I will demonstrate how simple it is to apply Trang to generation of W3C XML Schema and DTD definitions from a source XML file.
Oracle provides the highly useful Java OracleXMLQuery class for querying the relational database and provide the query results in XML format. The
OracleXMLQuery
class provides the setRowsetTag(String)
method to specify the Java String to be used as the root tag of the generated XML file containing the query results. Likewise, the OracleXMLQuery.setRowTag(String)
method allows one to specify the String label provided for each row element tag in the generated XML file.The XML below was generated using
OracleXMLQuery
with the rowset tag specified as "Employees" and the row tag specified as "Employee." The query used to generate this XML was run against the HR schema and is shown next.SELECT employee_id, first_name, last_name, department_name FROM employees, departments WHERE employees.department_id = departments.department_id
The XML generated by
OracleXMLQuery
is shown next.employees.xml - XML Generated with OracleXMLQuery
<?xml version = '1.0'?> <Employees> <Employee num="1"> <EMPLOYEE_ID>200</EMPLOYEE_ID> <FIRST_NAME>Jennifer</FIRST_NAME> <LAST_NAME>Whalen</LAST_NAME> <DEPARTMENT_NAME>Administration</DEPARTMENT_NAME> </Employee> <Employee num="2"> <EMPLOYEE_ID>201</EMPLOYEE_ID> <FIRST_NAME>Michael</FIRST_NAME> <LAST_NAME>Hartstein</LAST_NAME> <DEPARTMENT_NAME>Marketing</DEPARTMENT_NAME> </Employee> <Employee num="3"> <EMPLOYEE_ID>202</EMPLOYEE_ID> <FIRST_NAME>Pat</FIRST_NAME> <LAST_NAME>Fay</LAST_NAME> <DEPARTMENT_NAME>Marketing</DEPARTMENT_NAME> </Employee> <Employee num="4"> <EMPLOYEE_ID>114</EMPLOYEE_ID> <FIRST_NAME>Den</FIRST_NAME> <LAST_NAME>Raphaely</LAST_NAME> <DEPARTMENT_NAME>Purchasing</DEPARTMENT_NAME> </Employee> <Employee num="5"> <EMPLOYEE_ID>119</EMPLOYEE_ID> <FIRST_NAME>Karen</FIRST_NAME> <LAST_NAME>Colmenares</LAST_NAME> <DEPARTMENT_NAME>Purchasing</DEPARTMENT_NAME> </Employee> <Employee num="6"> <EMPLOYEE_ID>115</EMPLOYEE_ID> <FIRST_NAME>Alexander</FIRST_NAME> <LAST_NAME>Khoo</LAST_NAME> <DEPARTMENT_NAME>Purchasing</DEPARTMENT_NAME> </Employee> <Employee num="7"> <EMPLOYEE_ID>116</EMPLOYEE_ID> <FIRST_NAME>Shelli</FIRST_NAME> <LAST_NAME>Baida</LAST_NAME> <DEPARTMENT_NAME>Purchasing</DEPARTMENT_NAME> </Employee> <Employee num="8"> <EMPLOYEE_ID>117</EMPLOYEE_ID> <FIRST_NAME>Sigal</FIRST_NAME> <LAST_NAME>Tobias</LAST_NAME> <DEPARTMENT_NAME>Purchasing</DEPARTMENT_NAME> </Employee> <Employee num="9"> <EMPLOYEE_ID>118</EMPLOYEE_ID> <FIRST_NAME>Guy</FIRST_NAME> <LAST_NAME>Himuro</LAST_NAME> <DEPARTMENT_NAME>Purchasing</DEPARTMENT_NAME> </Employee> <Employee num="10"> <EMPLOYEE_ID>203</EMPLOYEE_ID> <FIRST_NAME>Susan</FIRST_NAME> <LAST_NAME>Mavris</LAST_NAME> <DEPARTMENT_NAME>Human Resources</DEPARTMENT_NAME> </Employee> <Employee num="11"> <EMPLOYEE_ID>198</EMPLOYEE_ID> <FIRST_NAME>Donald</FIRST_NAME> <LAST_NAME>OConnell</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="12"> <EMPLOYEE_ID>199</EMPLOYEE_ID> <FIRST_NAME>Douglas</FIRST_NAME> <LAST_NAME>Grant</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="13"> <EMPLOYEE_ID>120</EMPLOYEE_ID> <FIRST_NAME>Matthew</FIRST_NAME> <LAST_NAME>Weiss</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="14"> <EMPLOYEE_ID>121</EMPLOYEE_ID> <FIRST_NAME>Adam</FIRST_NAME> <LAST_NAME>Fripp</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="15"> <EMPLOYEE_ID>122</EMPLOYEE_ID> <FIRST_NAME>Payam</FIRST_NAME> <LAST_NAME>Kaufling</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="16"> <EMPLOYEE_ID>123</EMPLOYEE_ID> <FIRST_NAME>Shanta</FIRST_NAME> <LAST_NAME>Vollman</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="17"> <EMPLOYEE_ID>124</EMPLOYEE_ID> <FIRST_NAME>Kevin</FIRST_NAME> <LAST_NAME>Mourgos</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="18"> <EMPLOYEE_ID>125</EMPLOYEE_ID> <FIRST_NAME>Julia</FIRST_NAME> <LAST_NAME>Nayer</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="19"> <EMPLOYEE_ID>126</EMPLOYEE_ID> <FIRST_NAME>Irene</FIRST_NAME> <LAST_NAME>Mikkilineni</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="20"> <EMPLOYEE_ID>127</EMPLOYEE_ID> <FIRST_NAME>James</FIRST_NAME> <LAST_NAME>Landry</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="21"> <EMPLOYEE_ID>128</EMPLOYEE_ID> <FIRST_NAME>Steven</FIRST_NAME> <LAST_NAME>Markle</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="22"> <EMPLOYEE_ID>129</EMPLOYEE_ID> <FIRST_NAME>Laura</FIRST_NAME> <LAST_NAME>Bissot</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="23"> <EMPLOYEE_ID>130</EMPLOYEE_ID> <FIRST_NAME>Mozhe</FIRST_NAME> <LAST_NAME>Atkinson</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="24"> <EMPLOYEE_ID>131</EMPLOYEE_ID> <FIRST_NAME>James</FIRST_NAME> <LAST_NAME>Marlow</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="25"> <EMPLOYEE_ID>132</EMPLOYEE_ID> <FIRST_NAME>TJ</FIRST_NAME> <LAST_NAME>Olson</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="26"> <EMPLOYEE_ID>133</EMPLOYEE_ID> <FIRST_NAME>Jason</FIRST_NAME> <LAST_NAME>Mallin</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="27"> <EMPLOYEE_ID>134</EMPLOYEE_ID> <FIRST_NAME>Michael</FIRST_NAME> <LAST_NAME>Rogers</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="28"> <EMPLOYEE_ID>135</EMPLOYEE_ID> <FIRST_NAME>Ki</FIRST_NAME> <LAST_NAME>Gee</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="29"> <EMPLOYEE_ID>136</EMPLOYEE_ID> <FIRST_NAME>Hazel</FIRST_NAME> <LAST_NAME>Philtanker</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="30"> <EMPLOYEE_ID>137</EMPLOYEE_ID> <FIRST_NAME>Renske</FIRST_NAME> <LAST_NAME>Ladwig</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="31"> <EMPLOYEE_ID>138</EMPLOYEE_ID> <FIRST_NAME>Stephen</FIRST_NAME> <LAST_NAME>Stiles</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="32"> <EMPLOYEE_ID>139</EMPLOYEE_ID> <FIRST_NAME>John</FIRST_NAME> <LAST_NAME>Seo</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="33"> <EMPLOYEE_ID>140</EMPLOYEE_ID> <FIRST_NAME>Joshua</FIRST_NAME> <LAST_NAME>Patel</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="34"> <EMPLOYEE_ID>141</EMPLOYEE_ID> <FIRST_NAME>Trenna</FIRST_NAME> <LAST_NAME>Rajs</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="35"> <EMPLOYEE_ID>142</EMPLOYEE_ID> <FIRST_NAME>Curtis</FIRST_NAME> <LAST_NAME>Davies</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="36"> <EMPLOYEE_ID>143</EMPLOYEE_ID> <FIRST_NAME>Randall</FIRST_NAME> <LAST_NAME>Matos</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="37"> <EMPLOYEE_ID>144</EMPLOYEE_ID> <FIRST_NAME>Peter</FIRST_NAME> <LAST_NAME>Vargas</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="38"> <EMPLOYEE_ID>180</EMPLOYEE_ID> <FIRST_NAME>Winston</FIRST_NAME> <LAST_NAME>Taylor</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="39"> <EMPLOYEE_ID>181</EMPLOYEE_ID> <FIRST_NAME>Jean</FIRST_NAME> <LAST_NAME>Fleaur</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="40"> <EMPLOYEE_ID>182</EMPLOYEE_ID> <FIRST_NAME>Martha</FIRST_NAME> <LAST_NAME>Sullivan</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="41"> <EMPLOYEE_ID>183</EMPLOYEE_ID> <FIRST_NAME>Girard</FIRST_NAME> <LAST_NAME>Geoni</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="42"> <EMPLOYEE_ID>184</EMPLOYEE_ID> <FIRST_NAME>Nandita</FIRST_NAME> <LAST_NAME>Sarchand</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="43"> <EMPLOYEE_ID>185</EMPLOYEE_ID> <FIRST_NAME>Alexis</FIRST_NAME> <LAST_NAME>Bull</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="44"> <EMPLOYEE_ID>186</EMPLOYEE_ID> <FIRST_NAME>Julia</FIRST_NAME> <LAST_NAME>Dellinger</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="45"> <EMPLOYEE_ID>187</EMPLOYEE_ID> <FIRST_NAME>Anthony</FIRST_NAME> <LAST_NAME>Cabrio</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="46"> <EMPLOYEE_ID>188</EMPLOYEE_ID> <FIRST_NAME>Kelly</FIRST_NAME> <LAST_NAME>Chung</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="47"> <EMPLOYEE_ID>189</EMPLOYEE_ID> <FIRST_NAME>Jennifer</FIRST_NAME> <LAST_NAME>Dilly</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="48"> <EMPLOYEE_ID>190</EMPLOYEE_ID> <FIRST_NAME>Timothy</FIRST_NAME> <LAST_NAME>Gates</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="49"> <EMPLOYEE_ID>191</EMPLOYEE_ID> <FIRST_NAME>Randall</FIRST_NAME> <LAST_NAME>Perkins</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="50"> <EMPLOYEE_ID>192</EMPLOYEE_ID> <FIRST_NAME>Sarah</FIRST_NAME> <LAST_NAME>Bell</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="51"> <EMPLOYEE_ID>193</EMPLOYEE_ID> <FIRST_NAME>Britney</FIRST_NAME> <LAST_NAME>Everett</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="52"> <EMPLOYEE_ID>194</EMPLOYEE_ID> <FIRST_NAME>Samuel</FIRST_NAME> <LAST_NAME>McCain</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="53"> <EMPLOYEE_ID>195</EMPLOYEE_ID> <FIRST_NAME>Vance</FIRST_NAME> <LAST_NAME>Jones</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="54"> <EMPLOYEE_ID>196</EMPLOYEE_ID> <FIRST_NAME>Alana</FIRST_NAME> <LAST_NAME>Walsh</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="55"> <EMPLOYEE_ID>197</EMPLOYEE_ID> <FIRST_NAME>Kevin</FIRST_NAME> <LAST_NAME>Feeney</LAST_NAME> <DEPARTMENT_NAME>Shipping</DEPARTMENT_NAME> </Employee> <Employee num="56"> <EMPLOYEE_ID>104</EMPLOYEE_ID> <FIRST_NAME>Bruce</FIRST_NAME> <LAST_NAME>Ernst</LAST_NAME> <DEPARTMENT_NAME>IT</DEPARTMENT_NAME> </Employee> <Employee num="57"> <EMPLOYEE_ID>103</EMPLOYEE_ID> <FIRST_NAME>Alexander</FIRST_NAME> <LAST_NAME>Hunold</LAST_NAME> <DEPARTMENT_NAME>IT</DEPARTMENT_NAME> </Employee> <Employee num="58"> <EMPLOYEE_ID>107</EMPLOYEE_ID> <FIRST_NAME>Diana</FIRST_NAME> <LAST_NAME>Lorentz</LAST_NAME> <DEPARTMENT_NAME>IT</DEPARTMENT_NAME> </Employee> <Employee num="59"> <EMPLOYEE_ID>106</EMPLOYEE_ID> <FIRST_NAME>Valli</FIRST_NAME> <LAST_NAME>Pataballa</LAST_NAME> <DEPARTMENT_NAME>IT</DEPARTMENT_NAME> </Employee> <Employee num="60"> <EMPLOYEE_ID>105</EMPLOYEE_ID> <FIRST_NAME>David</FIRST_NAME> <LAST_NAME>Austin</LAST_NAME> <DEPARTMENT_NAME>IT</DEPARTMENT_NAME> </Employee> <Employee num="61"> <EMPLOYEE_ID>204</EMPLOYEE_ID> <FIRST_NAME>Hermann</FIRST_NAME> <LAST_NAME>Baer</LAST_NAME> <DEPARTMENT_NAME>Public Relations</DEPARTMENT_NAME> </Employee> <Employee num="62"> <EMPLOYEE_ID>176</EMPLOYEE_ID> <FIRST_NAME>Jonathon</FIRST_NAME> <LAST_NAME>Taylor</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="63"> <EMPLOYEE_ID>177</EMPLOYEE_ID> <FIRST_NAME>Jack</FIRST_NAME> <LAST_NAME>Livingston</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="64"> <EMPLOYEE_ID>179</EMPLOYEE_ID> <FIRST_NAME>Charles</FIRST_NAME> <LAST_NAME>Johnson</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="65"> <EMPLOYEE_ID>175</EMPLOYEE_ID> <FIRST_NAME>Alyssa</FIRST_NAME> <LAST_NAME>Hutton</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="66"> <EMPLOYEE_ID>174</EMPLOYEE_ID> <FIRST_NAME>Ellen</FIRST_NAME> <LAST_NAME>Abel</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="67"> <EMPLOYEE_ID>173</EMPLOYEE_ID> <FIRST_NAME>Sundita</FIRST_NAME> <LAST_NAME>Kumar</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="68"> <EMPLOYEE_ID>172</EMPLOYEE_ID> <FIRST_NAME>Elizabeth</FIRST_NAME> <LAST_NAME>Bates</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="69"> <EMPLOYEE_ID>171</EMPLOYEE_ID> <FIRST_NAME>William</FIRST_NAME> <LAST_NAME>Smith</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="70"> <EMPLOYEE_ID>170</EMPLOYEE_ID> <FIRST_NAME>Tayler</FIRST_NAME> <LAST_NAME>Fox</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="71"> <EMPLOYEE_ID>169</EMPLOYEE_ID> <FIRST_NAME>Harrison</FIRST_NAME> <LAST_NAME>Bloom</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="72"> <EMPLOYEE_ID>168</EMPLOYEE_ID> <FIRST_NAME>Lisa</FIRST_NAME> <LAST_NAME>Ozer</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="73"> <EMPLOYEE_ID>145</EMPLOYEE_ID> <FIRST_NAME>John</FIRST_NAME> <LAST_NAME>Russell</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="74"> <EMPLOYEE_ID>146</EMPLOYEE_ID> <FIRST_NAME>Karen</FIRST_NAME> <LAST_NAME>Partners</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="75"> <EMPLOYEE_ID>147</EMPLOYEE_ID> <FIRST_NAME>Alberto</FIRST_NAME> <LAST_NAME>Errazuriz</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="76"> <EMPLOYEE_ID>148</EMPLOYEE_ID> <FIRST_NAME>Gerald</FIRST_NAME> <LAST_NAME>Cambrault</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="77"> <EMPLOYEE_ID>149</EMPLOYEE_ID> <FIRST_NAME>Eleni</FIRST_NAME> <LAST_NAME>Zlotkey</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="78"> <EMPLOYEE_ID>150</EMPLOYEE_ID> <FIRST_NAME>Peter</FIRST_NAME> <LAST_NAME>Tucker</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="79"> <EMPLOYEE_ID>151</EMPLOYEE_ID> <FIRST_NAME>David</FIRST_NAME> <LAST_NAME>Bernstein</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="80"> <EMPLOYEE_ID>152</EMPLOYEE_ID> <FIRST_NAME>Peter</FIRST_NAME> <LAST_NAME>Hall</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="81"> <EMPLOYEE_ID>153</EMPLOYEE_ID> <FIRST_NAME>Christopher</FIRST_NAME> <LAST_NAME>Olsen</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="82"> <EMPLOYEE_ID>154</EMPLOYEE_ID> <FIRST_NAME>Nanette</FIRST_NAME> <LAST_NAME>Cambrault</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="83"> <EMPLOYEE_ID>155</EMPLOYEE_ID> <FIRST_NAME>Oliver</FIRST_NAME> <LAST_NAME>Tuvault</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="84"> <EMPLOYEE_ID>156</EMPLOYEE_ID> <FIRST_NAME>Janette</FIRST_NAME> <LAST_NAME>King</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="85"> <EMPLOYEE_ID>157</EMPLOYEE_ID> <FIRST_NAME>Patrick</FIRST_NAME> <LAST_NAME>Sully</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="86"> <EMPLOYEE_ID>158</EMPLOYEE_ID> <FIRST_NAME>Allan</FIRST_NAME> <LAST_NAME>McEwen</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="87"> <EMPLOYEE_ID>159</EMPLOYEE_ID> <FIRST_NAME>Lindsey</FIRST_NAME> <LAST_NAME>Smith</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="88"> <EMPLOYEE_ID>160</EMPLOYEE_ID> <FIRST_NAME>Louise</FIRST_NAME> <LAST_NAME>Doran</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="89"> <EMPLOYEE_ID>161</EMPLOYEE_ID> <FIRST_NAME>Sarath</FIRST_NAME> <LAST_NAME>Sewall</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="90"> <EMPLOYEE_ID>162</EMPLOYEE_ID> <FIRST_NAME>Clara</FIRST_NAME> <LAST_NAME>Vishney</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="91"> <EMPLOYEE_ID>163</EMPLOYEE_ID> <FIRST_NAME>Danielle</FIRST_NAME> <LAST_NAME>Greene</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="92"> <EMPLOYEE_ID>164</EMPLOYEE_ID> <FIRST_NAME>Mattea</FIRST_NAME> <LAST_NAME>Marvins</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="93"> <EMPLOYEE_ID>165</EMPLOYEE_ID> <FIRST_NAME>David</FIRST_NAME> <LAST_NAME>Lee</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="94"> <EMPLOYEE_ID>166</EMPLOYEE_ID> <FIRST_NAME>Sundar</FIRST_NAME> <LAST_NAME>Ande</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="95"> <EMPLOYEE_ID>167</EMPLOYEE_ID> <FIRST_NAME>Amit</FIRST_NAME> <LAST_NAME>Banda</LAST_NAME> <DEPARTMENT_NAME>Sales</DEPARTMENT_NAME> </Employee> <Employee num="96"> <EMPLOYEE_ID>101</EMPLOYEE_ID> <FIRST_NAME>Neena</FIRST_NAME> <LAST_NAME>Kochhar</LAST_NAME> <DEPARTMENT_NAME>Executive</DEPARTMENT_NAME> </Employee> <Employee num="97"> <EMPLOYEE_ID>100</EMPLOYEE_ID> <FIRST_NAME>Steven</FIRST_NAME> <LAST_NAME>King</LAST_NAME> <DEPARTMENT_NAME>Executive</DEPARTMENT_NAME> </Employee> <Employee num="98"> <EMPLOYEE_ID>102</EMPLOYEE_ID> <FIRST_NAME>Lex</FIRST_NAME> <LAST_NAME>De Haan</LAST_NAME> <DEPARTMENT_NAME>Executive</DEPARTMENT_NAME> </Employee> <Employee num="99"> <EMPLOYEE_ID>110</EMPLOYEE_ID> <FIRST_NAME>John</FIRST_NAME> <LAST_NAME>Chen</LAST_NAME> <DEPARTMENT_NAME>Finance</DEPARTMENT_NAME> </Employee> <Employee num="100"> <EMPLOYEE_ID>108</EMPLOYEE_ID> <FIRST_NAME>Nancy</FIRST_NAME> <LAST_NAME>Greenberg</LAST_NAME> <DEPARTMENT_NAME>Finance</DEPARTMENT_NAME> </Employee> <Employee num="101"> <EMPLOYEE_ID>111</EMPLOYEE_ID> <FIRST_NAME>Ismael</FIRST_NAME> <LAST_NAME>Sciarra</LAST_NAME> <DEPARTMENT_NAME>Finance</DEPARTMENT_NAME> </Employee> <Employee num="102"> <EMPLOYEE_ID>112</EMPLOYEE_ID> <FIRST_NAME>Jose Manuel</FIRST_NAME> <LAST_NAME>Urman</LAST_NAME> <DEPARTMENT_NAME>Finance</DEPARTMENT_NAME> </Employee> <Employee num="103"> <EMPLOYEE_ID>113</EMPLOYEE_ID> <FIRST_NAME>Luis</FIRST_NAME> <LAST_NAME>Popp</LAST_NAME> <DEPARTMENT_NAME>Finance</DEPARTMENT_NAME> </Employee> <Employee num="104"> <EMPLOYEE_ID>109</EMPLOYEE_ID> <FIRST_NAME>Daniel</FIRST_NAME> <LAST_NAME>Faviet</LAST_NAME> <DEPARTMENT_NAME>Finance</DEPARTMENT_NAME> </Employee> <Employee num="105"> <EMPLOYEE_ID>206</EMPLOYEE_ID> <FIRST_NAME>William</FIRST_NAME> <LAST_NAME>Gietz</LAST_NAME> <DEPARTMENT_NAME>Accounting</DEPARTMENT_NAME> </Employee> <Employee num="106"> <EMPLOYEE_ID>205</EMPLOYEE_ID> <FIRST_NAME>Shelley</FIRST_NAME> <LAST_NAME>Higgins</LAST_NAME> <DEPARTMENT_NAME>Accounting</DEPARTMENT_NAME> </Employee> </Employees>
Note that this generated XML has the "Employees" root tag and "Employee" row tags just as we specified with
OracleXMLQuery
. The rest of the XML elements are named based on the names of the columns in the SELECT statement and are all in uppercase. These individual element names correspond with the four columns in the SELECT clause.It is useful to have the XML shown above generated by
OracleXMLQuery
, but XML is often much more useful if we have a schema defining it. This is especially true if using technologies such as web services that require an XML schema. When you have example XML files but lack a schema definition for them, Trang comes to the rescue.The next screen snapshot (click on image to see larger version) displays the Trang help menu when unzipped from its downloadable ZIP file and executed with the Java launcher using the
java -jar trang.jar
command (trang.jar is an executable JAR) without any options.To generate a W3C XML Schema that defines the generated XML shown above, run the executable
trang.jar
command again, but this time specify an input file (the generated XML shown above) and specify an output file after that. In this case, I am running this command as: java -jar trang.jar employees.xml employees.xsd
. Trang detects that I want the a W3C XML Schema definition generated for the provided XML file because it recognizes the first listed argument as an XML file based on its extension (.xml) and recognizes the target format based on its extension (.xsd). The Trang manual also explains that you can use explicit options to specify the types of conversions to take place if you don't want to rely solely on file extensions.When the executable JAR
trang.jar
is executed as described above, the XSD file is generated that describes the input XML file. That generated XSD file is shown next:employees.xsd - W3C XML Schema Generated by Trang
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="Employees"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" ref="Employee"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Employee"> <xs:complexType> <xs:sequence> <xs:element ref="EMPLOYEE_ID"/> <xs:element ref="FIRST_NAME"/> <xs:element ref="LAST_NAME"/> <xs:element ref="DEPARTMENT_NAME"/> </xs:sequence> <xs:attribute name="num" use="required" type="xs:integer"/> </xs:complexType> </xs:element> <xs:element name="EMPLOYEE_ID" type="xs:integer"/> <xs:element name="FIRST_NAME" type="xs:string"/> <xs:element name="LAST_NAME" type="xs:string"/> <xs:element name="DEPARTMENT_NAME" type="xs:string"/> </xs:schema>
Without much effort on my part, I have a W3C XML Schema file that describes my generated XML. In practical terms, I am likely still going to need to narrow down some of the definitions because it is no surprise that Trang cannot detect more granular datatypes than general types like
xs:string
. So, I will almost certainly need to create more granular schema types if desired. However, this gives me a compliant starting point to add more specific details as desired.We are not limited to generating W3C XML Schema with Trang. We can also generate the older, less descriptive Document Type Definition (DTD) with the command:
java -jar trang.jar employees.xml employees.dtd
. The next listing demonstrates a DTD generated with just such a command.employees.dtd - Generated by Trang
<?xml encoding="UTF-8"?> <!ELEMENT Employees (Employee)+> <!ATTLIST Employees xmlns CDATA #FIXED ''> <!ELEMENT Employee (EMPLOYEE_ID,FIRST_NAME,LAST_NAME,DEPARTMENT_NAME)> <!ATTLIST Employee xmlns CDATA #FIXED '' num #REQUIRED> <!ELEMENT EMPLOYEE_ID (#PCDATA)> <!ATTLIST EMPLOYEE_ID xmlns CDATA #FIXED ''> <!ELEMENT FIRST_NAME (#PCDATA)> <!ATTLIST FIRST_NAME xmlns CDATA #FIXED ''> <!ELEMENT LAST_NAME (#PCDATA)> <!ATTLIST LAST_NAME xmlns CDATA #FIXED ''> <!ELEMENT DEPARTMENT_NAME (#PCDATA)> <!ATTLIST DEPARTMENT_NAME xmlns CDATA #FIXED ''>
Trang also allows generation of regular RelaxNG and compact RelaxNG schema definition formats as well. The simple changes to the command to get these two formats as well as the output from each are shown next.
employees.rng - Generated with
java -jar trang.jar employees.xml employees.rng
<?xml version="1.0" encoding="UTF-8"?> <grammar ns="" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <start> <element name="Employees"> <oneOrMore> <element name="Employee"> <attribute name="num"> <data type="integer"/> </attribute> <element name="EMPLOYEE_ID"> <data type="integer"/> </element> <element name="FIRST_NAME"> <text/> </element> <element name="LAST_NAME"> <text/> </element> <element name="DEPARTMENT_NAME"> <text/> </element> </element> </oneOrMore> </element> </start> </grammar>
employees.rnc - Generated with
java -jar trang.jar employees.xml employees.rnc
default namespace = "" start = element Employees { element Employee { attribute num { xsd:integer }, element EMPLOYEE_ID { xsd:integer }, element FIRST_NAME { text }, element LAST_NAME { text }, element DEPARTMENT_NAME { text } }+ }
With the two above examples of RELAX NG schema generation covered, the screen in which all four of these commands were run to generate XML Schema, DTD, RELAX NG, and compact RELAX NG are shown in the next screenshot. Note that there is no obvious output of the generated files, but the files will be found in the directory. For organizational purposes, I had my employees.xml file in a subdirectory called "input" and that is specified as part of the input file name.
Trang allows for multiple XML files to be specified as input as long as they are all passed in before the schema file that you want generated. This is useful if you want to generate one of the four schema types for a collection of related but not necessarily exactly the same XML source documents.
Another common use of Trang, especially in years past, was to migrate existing DTDs to W3C XML Schema. DTDs are necessarily less descriptive than W3C XML Schema, but such a conversion could at least get one started on a compliant Schema that could have greater description added.
Finally, in earlier versions of Java SE 6 there was a bug that prevented Trang from working correctly. However, I ran the examples shown here in Java SE 6, confirming that this bug has been fixed and closed.
The following are some links to Trang-related resources. Some of these were cited above as embedded links.
- Trang Web Page
- Trang Manual
- Trang Multi-Format Schema Converter Supports DTD to W3C XML Schema Conversion
- Converting RELAX NG to XSD
- Online O'Reilly RELAX NG Book
- Generate XML Schemas for Your Data (not Trang, but shows how to use Oracle's built-in PL/SQL function
DBMS_XMLSCHEMA.GENERATESCHEMA
to create an XSD from Oracle database data)
Publishing User Objects in JMX Notifications
The code for the main executable is the same as shown in my last blog entry. Likewise, the Spring configuration XML is also the same as shown in that blog entry and so is not shown here. The one piece of code that is different to supply a custom object to a Notification is the class that is Spring-exposed as an MBean. That class's source code is shown here with the new code highlighted.
SomeJavaObject.java
package dustin.server;
import javax.management.Notification;
import org.springframework.jmx.export.notification.NotificationPublisher;
import org.springframework.jmx.export.notification.NotificationPublisherAware;
/**
* Nothing in this class makes it explicitly a JMX MBean. Instead, Spring will
* expose this as an MBean. The Spring-specific NotificationPublisherAware
* interface is implemented by this class, which means that Spring will also
* allow this bean-turned-MBean to easily publish JMX notifications.
*
* @author Dustin
*/
public class SomeJavaObject implements NotificationPublisherAware
{
private int notificationIndex = 0;
private NotificationPublisher notificationPublisher;
private String someValue = "Nada";
public SomeJavaObject()
{
// empty default constructor
}
public String getSomeValue()
{
return this.someValue;
}
public void setSomeValue(final String aSomeValue)
{
notificationPublisher.sendNotification(
buildNotification(this.someValue, aSomeValue) );
this.someValue = aSomeValue;
}
/**
* Generate a Notification that will ultimately be published to interested
* listeners.
*
* @param aOldValue Value prior to setting of new value.
* @param aNewValue Value after setting of new value.
* @return Generated JMX Notification.
*/
private Notification buildNotification(
final String aOldValue,
final String aNewValue )
{
final String notificationType = "dustin.jmx.spring.notification.example";
final String message = "Converting " + aOldValue + " to " + aNewValue;
final Notification notification =
new Notification( notificationType,
this,
notificationIndex++,
System.currentTimeMillis(),
message );
UserDataObject userData =
new UserDataObject("UserData", "Java Management Extensions");
notification.setUserData(userData);
return notification;
}
/**
* This is the only method required to fully implement the
* NotificationPublisherAware interface. This method allows Spring to
* inject a NotificationPublisher into me.
*
* @param aPublisher The NotificationPublisher that Spring injects into me.
*/
public void setNotificationPublisher(NotificationPublisher aPublisher)
{
this.notificationPublisher = aPublisher;
}
}
In my previous blog entry, I provided a simple String to the
Notification.setUserData(Object)
method and did not need to do anything else, even on the client side, because String is Serializable and JMX clients understand Java Strings without any extra classpath information. However, with my own custom class now, I'll need to specify more on the classpath when running the JMX client.First, however, let's define the UserDataObject class used in the code above.
UserDataObject.java
import java.io.Serializable;
/**
* A simplistic data object meant to be used in JMX Notifications as a user
* data object.
*
* @author Dustin
*/
public class UserDataObject implements Serializable
{
private String objectName = null;
private String objectType = null;
/**
* Constructor accepting parameters to set all of my necessary parts.
*
* @param aObjectName
* @param aObjectType
*/
public UserDataObject(final String aObjectName, final String aObjectType)
{
this.objectName = aObjectName;
this.objectType = aObjectType;
}
/**
* Provide String representation of this object for use with JMX Notifications.
*
* @return String representation of me.
*/
@Override
public String toString()
{
return "Name of " + this.objectName + " and type of " + this.objectType;
}
}
Because this
UserDataObject
will need to be used by the JMX Client that listens for the published JMX Notifications, it is important that this class be declared as Serializable . The code to ensure that this class is Serializable was highlighted above.If the user data object was not serializable, an error similar to that shown in the following snapshot (click on it to see larger version) would appear.
The error message is repeated in text here for search purposes:
WARNING: Failed to deserialize a notification: java.io.NotSerializableException: dustin.server.UserDataObject
Even with the custom user data object declared as Serializable, JConsole will not be able to display the remote JMX Notifications if run locally with no extra options set. When we try to use JConsole to listen to these published JMX Notifications without specifying any extra options, we'll see an error like that shown in the next screen snapshot (click on image to see larger version).
The main text of this error message is:
WARNING: Failed to deserialize a notification: java.lang.ClassNotFoundException: dustin.server.UserDataObject (no security manager: RMI class loader disabled)
The problem here is that the custom user data object we included in the Notification is not available to JConsole in its classpath when run without options. As shown in Using JConsole - Java SE Monitoring and Management Guide, we can use the
-J-Djava.class.path
syntax to specify to JConsole (-J
) that a Java system property (-D
) needs to be specified and then specify the java.class.path
system property. The jconsole command to provide this custom user data object is shown next:
jconsole -J-Djava.class.path="%JAVA_HOME%\lib\jconsole.jar";"%JAVA_HOME%\lib\tools.jar";C:\userClient.jar
Because this example was run on Windows, the included class path uses double quotes around the
%JAVA_HOME%
environment variables and semicolons are used to separate classpath entries. Because I was overriding the JConsole classpath, I needed to provide the jconsole.jar
and tools.jar
JARs on the classpath in addition to my custom user data class (which I encapsulated in userClient.jar
).Here are the contents of
userClient.jar
:
0 Sun Feb 17 23:00:00 MST 2008 META-INF/
71 Sun Feb 17 23:00:00 MST 2008 META-INF/MANIFEST.MF
805 Sun Feb 17 22:50:34 MST 2008 dustin/server/UserDataObject.class
With the custom user data object specified as Serializable and provided on the classpath to the JMX client (JConsole), we can now see the successful results. The first of the next two screen snapshots shows a snippet of running the main application and entering three strings to be published in the JMX Notification. The second of the next two screen snapshots shows the JMX Notifications received by the JConsole listener when it is executed as shown above and in the first screen snapshot.
In this blog entry, I have attempted to demonstrate how custom user data objects can be associated with JMX Notifications even when they are published to remote clients. The two main things to remember are to make the user data object serializable and to make it available on the client's classpath.
UPDATE:
Two useful forum threads on custom objects on the JMX client side and ways to work with them are jconsole context classloader issue and jconsole no security manager exception (RMI classloader disabled).
Saturday, February 16, 2008
Publishing JMX Notifications with Spring
The first code shown here is the XML code that configures our Spring container.
spring-jmx-notif-server.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.0.xsd">
<bean id="someObject"
class="dustin.server.SomeJavaObject" />
<bean class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="dustin.example:name=jmx,type=someJavaObject"
value-ref="someObject" />
</map>
</property>
</bean>
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
<property name="port" value="1099"/>
</bean>
<bean id="serverConnector"
class="org.springframework.jmx.support.ConnectorServerFactoryBean"
depends-on="registry">
<property name="objectName" value="connector:name=rmi"/>
<property name="serviceUrl"
value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
</bean>
</beans>
Note that nothing specific to publishing JMX Notifications is included in this XML configuration, though there are entries to handle remote JMX. The next source code listing shows a pretty normal Java class, but it does feature a Spring-specific interface (NotificationPublisherAware) that it implements a single method (setNotificationPublisher) for.
SomeJavaObject.java
package dustin.server;
import javax.management.Notification;
import org.springframework.jmx.export.notification.NotificationPublisher;
import org.springframework.jmx.export.notification.NotificationPublisherAware;
/**
* Nothing in this class makes it explicitly a JMX MBean. Instead, Spring will
* expose this as an MBean. The Spring-specific NotificationPublisherAware
* interface is implemented by this class, which means that Spring will also
* allow this bean-turned-MBean to easily publish JMX notifications.
*
* @author Dustin
*/
public class SomeJavaObject implements NotificationPublisherAware
{
private int notificationIndex = 0;
private NotificationPublisher notificationPublisher;
private String someValue = "Nada";
public SomeJavaObject()
{
// empty default constructor
}
public String getSomeValue()
{
return this.someValue;
}
public void setSomeValue(final String aSomeValue)
{
notificationPublisher.sendNotification(
buildNotification(this.someValue, aSomeValue) );
this.someValue = aSomeValue;
}
/**
* Generate a Notification that will ultimately be published to interested
* listeners.
*
* @param aOldValue Value prior to setting of new value.
* @param aNewValue Value after setting of new value.
* @return Generated JMX Notification.
*/
private Notification buildNotification(
final String aOldValue,
final String aNewValue )
{
final String notificationType = "dustin.jmx.spring.notification.example";
final String message = "Converting " + aOldValue + " to " + aNewValue;
final Notification notification =
new Notification( notificationType,
this,
notificationIndex++,
System.currentTimeMillis(),
message );
notification.setUserData("Blog Example #" + notificationIndex );
return notification;
}
/**
* This is the only method required to fully implement the
* NotificationPublisherAware interface. This method allows Spring to
* inject a NotificationPublisher into me.
*
* @param aPublisher The NotificationPublisher that Spring injects into me.
*/
public void setNotificationPublisher(NotificationPublisher aPublisher)
{
this.notificationPublisher = aPublisher;
}
}
The above class is not an MBean by itself. Rather, Spring exposes it as an MBean because of our use of the MBeanExporter in the XML configuration. However, there were some JMX-specific classes in this class to enable Notifications. Also, as mentioned above, the Spring-specific
NotificationPublisherAware
interface is also explicitly implemented by this class.We need a main executable Java class to bootstrap the Spring container and to allow us the opportunity to interact and set someValue so that the JMX Notifications will occur. The source code for that class is shown next.
JmxSpringServerMain.java
package dustin.server;
import java.io.Console;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
/**
* Main executable designed to instantiate a Spring context and avoid exiting
* immediately so that a JMX client can listen to notifications sent from a
* Spring-exposed MBean Notification Publisher.
*
* This code sample requires Java SE 6 because it uses System.console() and
* String.isEmpty().
*
* @author Dustin
*/
public class JmxSpringServerMain
{
/**
* Main execution for JMX Spring Notification Publication example (server).
*
* @param args the command line arguments
*/
public static void main(String[] aCommandLineArgs)
{
ConfigurableApplicationContext context =
new FileSystemXmlApplicationContext(
"C:\\NetBeansProjects\\JMXSpringNotificationServer\\spring-jmx-notif-server.xml");
SomeJavaObject obj = (SomeJavaObject) context.getBean("someObject");
final Console systemConsole = System.console();
if ( systemConsole != null )
{
final String terminateLoop = "exit";
String choice = null;
do
{
systemConsole.printf("Enter a choice: ");
choice = systemConsole.readLine();
if ( choice != null && !choice.isEmpty() )
{
obj.setSomeValue(choice);
}
}
while ( choice != null
&& !choice.isEmpty()
&& !choice.equals(terminateLoop) );
}
else
{
System.err.println(
"Please run this in an environment with a system console.");
}
context.close();
}
}
The above class can be run to use the Spring container to expose SomeJavaObject as an MBean capable of sending JMX Notifications to local or remote clients. Note that Java SE 6 is required to run this example because of use of features specific to Java SE 6. When this main Java class is executed, the output will look something like the following screen snapshot (click on it to see larger version). I have entered some arbitrary values to demonstrate the JMX Notifications will be sent when we attach a JMX client later.
We can run JConsole to see if the JMX Notifications are being published. The following screen snapshot (click on it to see larger version) shows how JConsole appears when run alongside the application executed above. Note that I used Java SE 6's JConsole and that I had JConsole already running when executing the main Java application and that I had clicked on its "Subscribe" button in the MBeans Notifications section.
There are several interesting observations from this JConsole output. For one, JConsole nicely displays the timestamps I provided in the Notification as a long timestamp. Another interesting observation here is that the UserData column displays exactly the String I passed the Notification.setUserData(Object) method in the code. This only worked because I passed a String to this method that expects an Object. Had I not passed a String, I would have needed to pass a class that had a
toString()
method overridden appropriately and was available to a remote JMX client to have this be a sensible value. Using a String was the easy way out for this example.You may have noticed that I did not need to specify the "notificationPublisher" injected into the class that implements Spring's
NotificationPublisherAware
interface. Spring apparently does this automatically without any need to explicitly specify/configure this injection. In fact, I lost more time than I care to admit trying to specify this Notification Publisher in the Spring XML configuration file because this is shown in the second edition of Spring in Action (page 483). This book is generally a nice introduction to Spring and has been very beneficial for me, but something must have changed in Spring since the writing of this section. Anyway, it seems that you should NOT explicitly configure the injection of ModelMBeanNotificationPublisher despite what is shown on page 483. By the way, if you omit that one XML snippet from this book's coverage of JMX Notifications with Spring, the remainder of the example seems to accurately describe JMX Notification Publishing via Spring.The Spring documentation warns repeatedly that any natural JMX MBeans (objects that are MBeans before Spring exposes them as such) should NOT use Spring's JMX publication support and should instead use JMX Notification APIs directly.
Friday, February 15, 2008
Spring and JMX: Two Assemblers
The Spring framework makes it very simple to expose normal Java objects as JMX (Java Management Extensions) MBeans via Spring JMX configuration and no changes to the actual Java class.
The following code snippet shows a normal Java class (POJO) that will be exposed as a JMX MBean via the Spring framework.
StateHandler.javapackage dustin; /** * Handle state information. This normal Java class will be turned into an * MBean via Spring MBean exposure. * * @author Dustin */ public class StateHandler { private String stateName; private String stateCapital; private int applicationState; public StateHandler() { } public String getState() { return this.stateName; } public void setState(final String aStateName) { this.stateName = aStateName; } public String getCapital() { return this.stateCapital; } public void setCapital(final String aStateCapital) { this.stateCapital = aStateCapital; } public void setState(final int aState) { this.applicationState = aState; }
Note that the Java class defined above does not implement any interfaces and is so basic that it does not require even a single import statement. One may argue that this is a contrived example, but it will illustrate some issues later in this blog entry. More specifically, I intentionally named two methods setState
that are overloaded by different parameter data types. A better class design would have named these method names a lengthier, differentiating name, but I intentionally wanted these same-named methods to illustrate a point.
The main application that loads the Spring context is shown next.
JmxSpringMain.javapackage dustin; import java.io.IOException; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; /** * Simple example demonstrating Spring-exposed JMX beans. * * @author Dustin */ public class JmxSpringMain { /** * Main executable. * * @param args the command line arguments */ public static void main(String[] aCommandLineArgs) { ConfigurableApplicationContext context = new FileSystemXmlApplicationContext("C:\\NetBeansProjects\\JmxSpringExposedMethodNames\\spring-jmx-config.xml"); StateHandler state = (StateHandler) context.getBean("stateHandler"); System.out.print("Press <ENTER> to continue..."); try { final int throwAway = System.in.read(); } catch ( IOException ioEx ) { System.err.println("Error waiting for ENTER key to be pressed."); } context.close(); } }
The code listing for this class shows that it is a very simple executable class mainly meant to bootstrap a Spring container in a Java SE environment and then wait for pressing of the ENTER key. This forced suspension of execution is intentional so that the process will not complete too quickly. This allows for JConsole to be used to monitor the StateHandler class once Spring has turned it into a Model MBean.
The Spring XML configuration that is accessed by the main class and which instructs Spring to expose StateHandler as a JMX Model MBean is shown next.
spring-jmx-config.xml (without JMX MBean Exporter Assembler)<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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-2.0.xsd"> <bean id="stateHandler" class="dustin.StateHandler"> <property name="state" value="Colorado" /> <property name="capital" value="Denver" /> </bean> <bean class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="dustin.example:name=jmx,type=spring" value-ref="stateHandler" /> </map> </property> </bean> <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <property name="port" value="1099"/> </bean> <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry"> <property name="objectName" value="connector:name=rmi"/> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/> </bean> </beans>
With the code shown in the listings above, the Spring framework will expose all public methods defined in StateHandler as JMX MBean operations and will expose attributes based on get methods defined in StateHandler. This is illustrated by the following snapshot of JConsole when attached to this Spring-powered and Spring-exposed MBean (click on images to see larger versions).
As the above screen snapshot shows, JConsole is providing access for administrators to change the state name and state capital via JMX. This is probably not intended and we really only want to change the application state rather than the state name. We can use an assembler to instruct Spring on which methods and properties to expose as MBean operations and attributes. In this blog entry, I'll focus on two of these approaches: the method name approach and the interface-based approach.
Spring allows us to specify which methods we want exposed on a bean-exposed-as-MBean. The Spring XML configuration if file to make this happen is shown again with the changes to make this happen on lines 20 and 24-31.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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-2.0.xsd"> <bean id="stateHandler" class="dustin.StateHandler"> <property name="state" value="Colorado" /> <property name="capital" value="Denver" /> </bean> <bean class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="dustin.example:name=jmx,type=spring" value-ref="stateHandler" /> </map> </property> <property name="assembler" ref="assembler" /> </bean> <bean id="assembler" class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler"> <property name="managedMethods"> <list> <value>setState</value> </list> </property> </bean> <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <property name="port" value="1099"/> </bean> <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry"> <property name="objectName" value="connector:name=rmi"/> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/> </bean> </beans>
When this new Spring XML configuration file is used, the exposed MBean operations are limited to what is specified in the Method Name Bean Assembler. The output when using JConsole now to monitor the application is shown next.
We only exposed a single setState
method name in our configuration above. So why are there two setState
operations in the JConsole window? The explanation for this is that Spring has no way of knowing which setState method is being specified because it cannot differentiate between these overloaded methods based on method name alone. The next approach, using a Java interface to specify which methods are exposed, will work better in this situation.
The following Spring XML configuration uses an interface-based approach rather than a method name approach to narrow which methods and properties Spring should expose as MBean operations and attributes. Again, the relevant new XML code is on line 20 and lines 24 through 31.
spring-jmx-config.xml (using JMX MBean Exporter InterfaceBasedMBeanInfoAssembler)<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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-2.0.xsd"> <bean id="stateHandler" class="dustin.StateHandler"> <property name="state" value="Colorado" /> <property name="capital" value="Denver" /> </bean> <bean class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="dustin.example:name=jmx,type=spring" value-ref="stateHandler" /> </map> </property> <property name="assembler" ref="assembler" /> </bean> <bean id="assembler" class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler"> <property name="managedInterfaces"> <list> <value>dustin.AppStateIf</value> </list> </property> </bean> <bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <property name="port" value="1099"/> </bean> <bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean" depends-on="registry"> <property name="objectName" value="connector:name=rmi"/> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/> </bean> </beans>
Note that a Java interface is specified in the above XML. This very simple interface is shown next.
AppStateIf.javapackage dustin; /** * Interface to be used to narrow operations exposed as MBean operations on a * bean-turned-MBean via Spring. * * @author Dustin */ public interface AppStateIf { public void setState(final int aState); }
When this newly configured Spring-powered application is run with this interface-based JMX exposure, JConsole displays the results shown in the next snapshot.
With the interface-based approach we were able to instruct Spring more specifically on which of the overloaded methods we wanted exposed as an MBean operation. Note that we did not even need to have the POJO class StateHandler implement this interface (though it is often nice to do so to allow the compiler to ensure that a POJO that will be exposed as an MBean provides anticipated methods for JMX operations). Our regular Java object could remain untouched and this interface could be applied in the Spring configuration only to narrow down the exposed JMX operations.
Another common way to configure Spring to expose certain Java methods as JMX operations and certain Java properties as JMX attributes is to use Spring-expected annotations in the source code. Unlike the method name approach and interface-based approach shown in this blog entry, this approach does require Spring-specific annotations to be added to your previously non-Spring-specific Java classes. The advantage gained for this price, however, is the ability to even more finely control what is exposed as an MBean and to provide significantly more descriptive information to the exposed MBean, its attributes, and its operations.
One final note should be made about the Spring XML configuration examples shown in this entry. In all three entries, I specified RMI server connection information and instructed Spring to set up an RMI registry to support remote JMX features. This remote support was not necessary to use the examples, however, because I used JConsole on the same host as the managed application and both (JConsole and the managed application) were run under my same user ID.
Thursday, February 14, 2008
RMOUG TD 2008 Answers to XQuery Questions
Before getting into some of the questions that were asked along with answers to them and additional references associated with them, I'd like to thank all those who attended my two presentations and especially thank those who asked questions.
How do you use Oracle's proprietary
ora:view
function with more than one table (joins)?To this question, I answered that the only way that I could think of and had seen to do this is to use ora:view on a Oracle database view. This appears to be the appropriate answer as documented in the Using XQuery with Oracle XML DB document.
Can you query the contents of Microsoft Office documents with XQuery?
It is possible to use XQuery to query the contents of Microsoft Office 2007 documents (or documents saved in the Office Open XML format using the available converters for older versions of Office applications). In fact, this is documented in the article XQuery Your Office Documents. Note, however, that this requires that the XQuery engine be able to open ZIP files and I have not been able to find a way to do that automatically with Oracle's XQuery implementation. One could still open the Office Open XML ZIP file in another way (using ZIP utility or JAR or similar mechanism) and then XQuery the contents.
The meeting participant who asked this question needs to query Office applications data prior to the Office Open XML format, so I told her that XQuery will not be able to help here. While my other topic at this conference (Apache POI) is useful for reading Excel, it does not work natively out of the database like XQuery does with Oracle's XQuery implementation.
How do you use XQuery with an XMLType Database Column?
Although I had many XQuery examples in my presentation, I did not specifically demonstrate the use of the SQL/XML standard XMLQUERY on an XMLTYPE database column. Fortunately, a good example of this is demonstrated as Example 18-8 in the Using XQuery with XMLType Data section of the Using XQuery with Oracle XML DB chapter of the Oracle XML DB Developer's Guide 11g. The key to this is the use of the PASSING clause.
How do you terminate an XQuery expression?
I think I may have misunderstood this question and given an answer to a different question. I think what the attendee was asking is how one could have multiple XQuery statements in a single document running procedurally. It is important to note that an XQuery Scripting Extension is currently under work in the Worldwide Web Consortium (W3C). The XQuery Prototype with XQLPlus README document discusses using an XQuery
.xql
file. Perhaps the best example of complex XQuery used similar to XSLT is the article Generating XML and HTML with XQuery. This example uses XQuery custom functions embedded within a master XQuery expression. In other words, with XQuery, nesting XQuery expressions is the approach often used to build elaborate transformations.Finally, the Querying XMLType Data section of the article Querying, Constructing, and Transforming XML with XQuery also shows how nested XQuery expressions can be used in transformation.
Is Apache POI Related to or Dependent On Apache HTTP Server?
For those of us who use several Apache products, this is an easy question and answer. However, I thought it was a good question that reminds me that many in the IT community, especially those outside of the Java development community, may not be as familiar with the Apache Software Foundation. Just as Oracle Corporation is still most often associated with database products by many DBAs and developers, it is likely that Apache implies HTTP server (or perhaps Tomcat servlet container) to many people as well. I pointed out that not only is Apache POI not dependent on Apache HTTP server being present, but that they in fact have very little to do with each other besides their common "Apache" name.
UPDATE (16 February 2007): The following are some other accounts in the blogosphere regarding recently concluded RMOUG Training Days 2008.
RMOUG Training Days 2008 Starting -- Alex Gorbachev
http://www.pythian.com/blogs/825/rmoug-training-days-2008-starting
First Day at RMOUG -- Babette Turner-Underwood
http://www.pythian.com/blogs/827/first-day-at-rmoug
RMOUG Day 2!! -- Babette Turner-Underwood
http://www.pythian.com/blogs/829/rmoug-day-2
RMOUG 2008 is Over -- Alex Gorbachev
http://www.pythian.com/blogs/831/rmoug-2008-is-over
George Trujillo on RMOUG Training Days 2008
http://trubix.blogspot.com/
* "RMOUG - Excellent Ending, Now on to Collaborate 2008 in Denver"
* "RMOUG Training Days - February 13, 14 2008 in Denver, CO"
* "RMOUG 2008: MySQL and Oracle Fusion Middleware Presentations"
Can a Performance Tuning Tool be Complete?
http://www.battleagainstanyguess.com/2008/02/can-a-performance-tuning-tool-be-complete/