FindBugs is a great static analysis tool and it helped me to find several bugs but recently we switched to Wicket 1.3 framework for web development and we got few false positives which were related to using Spring with Wicket.
Serialization
We had Wicket page classes with fields being injected by Spring using the @SpringBean annotation.
FindBugs complained that these fields should have been serializable or transient (SE_BAD_FIELD error) and this seemed an issue as Wicket does use serialization a lot to save pages into its session.
In fact, it turned out to be a false postive because Wicket manages this by using Spring dynamic proxies.
Unitialized fields in constructor
Another FindBugs complaint in Wicket page classes was about de-referencing fields in constructor before having initialize them (UR_UNINIT_READ error).
Again, this turned out to be a false positive because our pages did extend WebPage class that takes care of injecting all SpringBean annotated fields using a PropertyResolver.
Something to remember: the default PropertyResolver is able to initialize private fields and ignores setters.
Conclusion
Using Spring beans in Wicket pages introduces a lot of dynamicity that defeats static analysis when dealing with object initialization and serialization.
FindBugs is so valuable that it is worth excluding these rules on Pages and having a naming convention for these classes makes it easier to do. Our page classes are now named with the "Page" suffix.
2008-08-10
2008-04-19
My first (useful) script in Groovy
We had a large number of XML files to modify and found that Groovy with its GPath syntax was the right tool. Here is an example that takes a (simplified) XML file and changes the value of one attribute for a subset of nodes.
And the script to change all XML files in the current directory.
I found it easier to write and debug it than using a mix of java and XSL.
<?xml version="1.0"?>
<design>
<process>
<variable id="V1" visible="true" />
<variable id="V2" visible="false" />
<variable id="V3" visible="false" />
</process>
</design>
And the script to change all XML files in the current directory.
I found it easier to write and debug it than using a mix of java and XSL.
def basedir = new File( ".")
// Create a directory for patched files
new File("patched").mkdir()
// Get files with ".xml" extension
files = basedir.listFiles().grep(~/.*\.xml$/)
// Iterate on the files
files.each {
patchXML(it)
}
def patchXML(file) {
println "-------------------"
println "Patching $file.name"
def design = new XmlParser().parse(file)
def modified = false
for (variable in design.process.variable) {
switch (variable.@id) {
case "V1":
case "V2":
if (variable.@visible == "false") {
variable.@visible = "true"
modified = true
}
}
}
if (modified) {
new File("patched/$file.name").withPrintWriter() {
new XmlNodePrinter(it).print(design)
}
println "Patched $file.name is in \"patched\" directory"
} else {
println "$file.name was not modified"
}
println "-------------------"
}
2008-04-10
Hibernate without a DBA: a sure path to failure!
Recently, my team had to deploy a Java web application that we bought from a small company. They used Hibernate as their Object Relational Mapping and were using it to generate their database schema.
In order to make their installation process easier, the application was creating or updating the database schema at startup time. This may sound like a nice idea but in most companies, you don't want to grant your application DROP, CREATE or ALTER privileges as it could be a security vulnerability if your web application gets hacked. Fortunately, Hibernate provides you with ant tasks to generate your schema creation script and if your customer runs on a different database server, you will generate a script for it.
That's great but as soon as we started to run our load tests, we got tons of deadlock errors on SQL Server 2005:
Application bugs
The show_sql flag in Hibernate configuration is a useful tool, it helped the developers to find that under some circumstances the application would save the same data twice. They fixed the code,we ran the load test again and observed a great performance improvement but still deadlock errors.
Another search on Google leaved me with more questions than answers with few exceptions like this excellent article from Bart Duncan. After reading it, you should be convinced that you need some help from a good DBA. We were fortunate to have a good DBA team even though I found it difficult to explain them what statements or queries we were using because they were generated by Hibernate and not written by ourselves.
The most difficult part was to obtain a trace on the database server when reproducing a deadlock. Many trace flags can be too invasive and change your timing preventing you to reproduce the deadlock. We found that the trace flag 1222 was the most helpful to get data without modifying the execution timing.
Unicode encoding
Our DBA analyzed the data and found a deadlock cause: inserting unicode strings into a varchar column that was indexed, this caused a conversion and index scan resulting in a concurrency between 2 threads executing same statement updating different rows but using same index. Solution: convert column types to nvarchar or change your JDBC driver settings to avoid encoding string parameters in Unicode, both will also bring you an additional gain in performance.
Missing index on a foreign key
We changed the JDBC driver setting and ran again the load test: deadlocks again but new ones.
Our DBA did analyze the new trace and quickly found a foreign key in a one-to-many relationship which was not indexed, this resulted in an index scan and deadlock on concurrent updates. This is something that you can and should specify in your Hibernate mappings.
Other issues
You can get deadlocks also when using clustered indexes. It turns out that by default primary keys in SQL Server are clustered indexes. So by using Hibernate schema, you will get clustered indexes for all your primary keys, it's usually not a problem especially for naturally growing keys like identity columns but if you are using randomly generated strings for your ids it can be a problem.
Conclusion
Hibernate like other ORMs is a useful tool but you should use its schema generation feature for what it is: a help to speed up your initial development but you will not avoid fine tuning of your table definitions, indexes and only an experimented DBA can help you there.
Do not believe that by using Hibernate you will be able to migrate easily from one database engine to another one, you will almost always end up tuning your schema to solve deadlocks or performance issues in a specific way.
In order to make their installation process easier, the application was creating or updating the database schema at startup time. This may sound like a nice idea but in most companies, you don't want to grant your application DROP, CREATE or ALTER privileges as it could be a security vulnerability if your web application gets hacked. Fortunately, Hibernate provides you with ant tasks to generate your schema creation script and if your customer runs on a different database server, you will generate a script for it.
That's great but as soon as we started to run our load tests, we got tons of deadlock errors on SQL Server 2005:
Transaction (Process ID 54) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.A typical developer reaction is to accuse Hibernate, the driver or the database engine. After a quick search on Google I was under the impression that it should be a bug in the application.
Application bugs
The show_sql flag in Hibernate configuration is a useful tool, it helped the developers to find that under some circumstances the application would save the same data twice. They fixed the code,we ran the load test again and observed a great performance improvement but still deadlock errors.
Another search on Google leaved me with more questions than answers with few exceptions like this excellent article from Bart Duncan. After reading it, you should be convinced that you need some help from a good DBA. We were fortunate to have a good DBA team even though I found it difficult to explain them what statements or queries we were using because they were generated by Hibernate and not written by ourselves.
The most difficult part was to obtain a trace on the database server when reproducing a deadlock. Many trace flags can be too invasive and change your timing preventing you to reproduce the deadlock. We found that the trace flag 1222 was the most helpful to get data without modifying the execution timing.
Unicode encoding
Our DBA analyzed the data and found a deadlock cause: inserting unicode strings into a varchar column that was indexed, this caused a conversion and index scan resulting in a concurrency between 2 threads executing same statement updating different rows but using same index. Solution: convert column types to nvarchar or change your JDBC driver settings to avoid encoding string parameters in Unicode, both will also bring you an additional gain in performance.
Missing index on a foreign key
We changed the JDBC driver setting and ran again the load test: deadlocks again but new ones.
Our DBA did analyze the new trace and quickly found a foreign key in a one-to-many relationship which was not indexed, this resulted in an index scan and deadlock on concurrent updates. This is something that you can and should specify in your Hibernate mappings.
Other issues
You can get deadlocks also when using clustered indexes. It turns out that by default primary keys in SQL Server are clustered indexes. So by using Hibernate schema, you will get clustered indexes for all your primary keys, it's usually not a problem especially for naturally growing keys like identity columns but if you are using randomly generated strings for your ids it can be a problem.
Conclusion
Hibernate like other ORMs is a useful tool but you should use its schema generation feature for what it is: a help to speed up your initial development but you will not avoid fine tuning of your table definitions, indexes and only an experimented DBA can help you there.
Do not believe that by using Hibernate you will be able to migrate easily from one database engine to another one, you will almost always end up tuning your schema to solve deadlocks or performance issues in a specific way.
2008-02-27
Public Maven repositories and wrong POMs
I experienced an issue with log4j 1.2.15: it comes with a POM that wrongly forces you to include extra dependencies that should be optional (e.g. Java mail if you don't plan to use the SMTPAppender feature). This issue has been reported as bug #43304 to log4j team.
My first reaction was to exclude these dependencies in my project pom.xml but this did not work (I did not take time to investigate why). Anyway, it was faster for me to patch the log4j POM in our intranet repository.
Lesson learned: managing dependencies can be hard and you should not depend on public repositories as they can be wrong sometimes. Make sure your team has an Intranet repository and make it simple to update with a good repository manager like Artifactory.
My first reaction was to exclude these dependencies in my project pom.xml but this did not work (I did not take time to investigate why). Anyway, it was faster for me to patch the log4j POM in our intranet repository.
Lesson learned: managing dependencies can be hard and you should not depend on public repositories as they can be wrong sometimes. Make sure your team has an Intranet repository and make it simple to update with a good repository manager like Artifactory.
2007-10-26
Not fully convinced by Maven 2
After several weeks of trying to migrate our build process to Maven 2, we decided to stop due to:
So, our next approach will be to use ant for building and restrict Maven to managing dependencies and generating project site and quality reports.
- Incompatibility with WebLogic: we use BEA Weblogic ant tasks that are not compatible with Maven build lifecycle phases. For instance, appc mixes generate-sources, compile and package; for this reason the maven-weblogic-plugin that uses these tasks has same flaw. So if you need to use other Maven plugins you must implement workarounds and attach them to unnatural phases to ensure they get executed before or after appc. See my previous post.
- Lack of flexibility: by nature, Maven is not flexible, the only ways to get more flexibility is to use profiles, maven-antrun-plugin or to write your own plugin in java: not really simple nor very productive especially if you need to write simple workarounds. It's strange because there seems to be a growing tendency in open source build tools to enable more flexibility: ant 1.8 feature list includes logic/control flow features from ant-contrib (for, if, while, etc…), gant uses groovy as build scripting language on top of ant, buildr uses Ruby.
- Lack of auditability: with Maven's convenient way to download missing parts and self update, it's difficult to know exactly what was used to build one version of your product. The fact that Maven mixes tool's dependencies of the tool (maven+plugins) and dependencies of the projects being built into one single local repository surely does not help here. it would be better if separation of concerns was implemented in local repository to ensure that the dependencies managed by the developers do not get polluted by dependencies that are out of its control scope.
- Issues are difficult to diagnose: I've got examples about classpath, system properties where it's difficult to find the culprit between Maven, plugins or dependencies.
- Plugins quality ranks from excellent to terrible: this is not a Maven issue by itself but combined with above mentioned difficulty to diagnose it can lead to painful situations.
- Maven's defect backlog is quite impressive and I'm not sure that the situation is going to improve soon.
So, our next approach will be to use ant for building and restrict Maven to managing dependencies and generating project site and quality reports.
2007-09-15
How to connect to an MS Access database using Ruby and ODBC
If you search for an answer to this question in Google, you will get mostly sarcastic answers and if you're lucky maybe some help.
You must first download Ruby ODBC from Christian Werner's site and copy odbc.so and odbc_utf8.so into the ...\ruby\1.8\i386-msvcrt directory where you installed Ruby. Uf you have installed InstantRails in default location, it should be under C:\InstantRails\ruby\lib\ruby\1.8\i386-mswin32
In the example below I connect to an Access file (c:\ruby.mdb) and print the contents of a table (Table1)
Similarly, you can use DBI:ODBC to read MS Excel files.
In the example below I connect to an Excel file (c:\my_file.xls) and print the contents of a spreadsheet (Sheet1)
My configuration is Windows XP + MS Access 2000 + MS Excel 2002 + Ruby 1.8.6 + Ruby ODBC 0.9995
You must first download Ruby ODBC from Christian Werner's site and copy odbc.so and odbc_utf8.so into the ...\ruby\1.8\i386-msvcrt directory where you installed Ruby. Uf you have installed InstantRails in default location, it should be under C:\InstantRails\ruby\lib\ruby\1.8\i386-mswin32
In the example below I connect to an Access file (c:\ruby.mdb) and print the contents of a table (Table1)
require 'DBI'
access_dsn_base = 'DBI:ODBC:Driver=Microsoft Access Driver (*.mdb);DBQ='
access_file = 'C:\ruby.mdb'
DBI.connect(access_dsn_base+access_file) do |dbh|
dbh.select_all('Select * From Table1') {|row| p row}
end
Similarly, you can use DBI:ODBC to read MS Excel files.
In the example below I connect to an Excel file (c:\my_file.xls) and print the contents of a spreadsheet (Sheet1)
require 'DBI'
excel_dsn_base = 'DBI:ODBC:Driver={Microsoft Excel Driver (*.xls)};DBQ='
excel_file = 'C:\my_file.xls'
DBI.connect(excel_dsn_base+excel_file) do |dbh|
dbh.select_all('Select * From [Sheet1$]') {|row| p row}
end
My configuration is Windows XP + MS Access 2000 + MS Excel 2002 + Ruby 1.8.6 + Ruby ODBC 0.9995
2007-09-12
WebLogic 9.2 is not Maven 2 friendly
Using Maven 2 is more difficult when you use a platform like BEA WebLogic 9.2 because of their "split directory" convention which creates a gap with Maven's own conventions.
A first approach could be to avoid using the split directory structure, however it's not that easy because it comes with a set of specific ant tasks to pre-compile your JSPs, generate a web service from an EJB, package an ear file, ...
These tasks are not open sourced and are too high level to be easily integrated into Maven: for instance, if you want to pre-compile your JSPs, you cannot use anymore jspc because it has been deprecated in favor of appc which depends on the split directory structure.
This makes almost impossible to write an equivalent Maven plugin for a 3rd party, only BEA could do it.
Another issue with these tasks is that they are defined in weblogic.jar: a 50 MB jar which contains a lot of stuff that may collides with dependencies in your classpath, especially because BEA chose to specify a relative classpath in its jar manifest.
An alternative is to use the WebLogic ant tasks from Maven using the antrun plugin, unfortunately this plugin is poorly documented and does not offer easy options to configure a classpath to integrate weblogic.jar. Also, antrun retrieves its own version of ant from Maven's repository that may collide with the one required by WebLogic's tasks; you can find workarounds for this but then you will have to trick Maven to make it think that it has generated the artifact generated by appc.
So, unless BEA endorses Maven and provides its own plugins, it will be difficult to use Maven for a non trivial WebLogic application. My next step will be to try to use ant and Maven's antlib in order to be able to use Maven's dependency management.
P.S.: I just found Hussein Badakhchani's excellent post about maven and appc, I may try his approach as well.
A first approach could be to avoid using the split directory structure, however it's not that easy because it comes with a set of specific ant tasks to pre-compile your JSPs, generate a web service from an EJB, package an ear file, ...
These tasks are not open sourced and are too high level to be easily integrated into Maven: for instance, if you want to pre-compile your JSPs, you cannot use anymore jspc because it has been deprecated in favor of appc which depends on the split directory structure.
This makes almost impossible to write an equivalent Maven plugin for a 3rd party, only BEA could do it.
Another issue with these tasks is that they are defined in weblogic.jar: a 50 MB jar which contains a lot of stuff that may collides with dependencies in your classpath, especially because BEA chose to specify a relative classpath in its jar manifest.
An alternative is to use the WebLogic ant tasks from Maven using the antrun plugin, unfortunately this plugin is poorly documented and does not offer easy options to configure a classpath to integrate weblogic.jar. Also, antrun retrieves its own version of ant from Maven's repository that may collide with the one required by WebLogic's tasks; you can find workarounds for this but then you will have to trick Maven to make it think that it has generated the artifact generated by appc.
So, unless BEA endorses Maven and provides its own plugins, it will be difficult to use Maven for a non trivial WebLogic application. My next step will be to try to use ant and Maven's antlib in order to be able to use Maven's dependency management.
P.S.: I just found Hussein Badakhchani's excellent post about maven and appc, I may try his approach as well.
Inscription à :
Articles (Atom)