Общее·количество·просмотров·страницы

Java Dev Notes - разработка на Java (а также на JavaScript/Python/Flex и др), факты, события из АйТи

вторник, 16 июня 2009 г.

FindBugs

FindBugs - утилита-анализатор исходного кода на Java на наличие потенциально опасных конструкций в нем. Тулза сделана в Университете Мэриленда, распространяется под лицензией LGPL.

FindBugs отлавливает так называемые bug patterns - т.e. конструкции в коде, которые, скорее всего, являются ошибкой. Причем, для ее работы не обязательно иметь исходный код приложения - на самом деле она выполняет анализ байт-кода, используя для этих целей библиотечку Apache BCEL (ByteCode Engineering Library), которая уже упоминалась в этом сообщении. Таким образом, можно даже сторонние JARы проанализировать на наличие ошибок - FindBugs может обрабатывать JAR-, ZIP-файлы, и, конечно, class-файлы.

Установка сей тулзы подробно описана здесь. Я устанавливал FindBugs таким образом: скачал findbugs-1.3.8.zip c SourceForge.net, разархивировал его в каталог D:\tools\findbugs-1.3.8, установил переменную среды FINDBUGS_HOME=D:\tools\findbugs-1.3.8.

Далее мы разберем работу с FindBugs c использованием Ant. Кстати, у FindBugs есть также плагин к Eclipse. Но поскольку я являюсь сторонником использования командной строки (и особенно люблю Ant =) ), то именно совместное использование Ant & FindBugs - предмет данного поста.

FindBugs довольно требователен к памяти - в руководстве на сайте программы еще в первой главе отмечают, что для работы FindBugs потребуется минимум полгигабайта оператиной памяти, а лучше больше, конечно. При первых запусках на моей машине FindBugs выбрасывал исключение java.lang.OutOfMemoryError до тех пор, пока я не увеличил объем памяти, доступной JVM до 512Mb. В команде <findbugs> (о ней речь пойдет ниже) есть параметр jvmargs, который, как понятно из названия, содержит параметры, передаваемые JVM. Установив jvmargs="-Xmx512M" я избавился от java.lang.OutOfMemoryError.

Далее предполагается, что Ant установлен в каталог ANT_HOME, а FindBugs установлен в каталог FINDBUGS_HOME. Как я уже говорил, у меня на компьютере установлена переменная среды FINDBUGS_HOME=D:\tools\findbugs-1.3.8. Первым делом самолеты надо скопировать два файла FINDBUGS_HOME/lib/bcel.jar и FINDBUGS_HOME/lib/findbugs-ant.jar в каталог ANT_HOME/lib. Затем в файле сборки (build.xml) декларируется новая команда - <findbugs>:


<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"/>

Рассмотрим параметры команды <findbugs> (в таблице ниже перечислены не все параметры, а только самые важные, на мой взгляд):

















ПараметрОписаниеОбязательный
classДочерний элемент, указывающий, что именно анализировать. Должен содержать атрибут location, в котором указывается путь к анализируемому объекту. Это может быть директория, JAR или ZIP файл, class-файл. Тэг <findbugs> может содержать несколько дочерних элементов <class>. Пример:

<class location="${build.dir}"/>
<class location="resulted.jar"/>
<class location="Test.class"/>


Да
auxClasspathДочерний элемент, содержащий библиотеки, которые используются в анализируемом коде, но которые пользователь не будет анализировать. Этот элемент объявляется так же, как и classpath в команде <java>. Пример:
<auxClasspath refid="main.classpath"/>
- здесь идет ссылка на main.classpath, который ранее объявлен в файле сборки. Например, он может быть объявлен следующим образом:

<path id="main.classpath">
<fileset dir="${lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>

Нет
homeЭтот атрибут должен содержать имя каталога, где находится FindBugs. Пример:
<findbugs home="${findbugs.home.dir}">
Да
reportLevelЭтот атрибут определяет уровень ошибок, которые выводятся в файл результатов. Есть три уровня:
  • low - рапортуются все ошибки

  • medium - (значение по умолчанию) рапортуются только ошибки уровня medium, high

  • high - рапортуются только ошибки уровня high
Пример:
<findbugs reportLevel="low">
Нет
outputЭтот атрибут определяет формат файла отчета. Принимает следующие значения:
  • xml - (значение по умолчанию) формат XML. Файл отчета в этом формате можно затем открыть с помощью GUI-интерфейса

  • xml:withMessages - вывод в формате XML дополнен поясняющими сообщениями. Файл отчета в этом формате также можно затем открыть с помощью GUI-интерфейса. Этот файл с сообщениями можно затем обработать XSLT-процессором, чтобы получить, допустим, расширенный отчет в HTML.

  • html - вывод в формате HTML

  • text - вывод в текстовом формате

  • emacs - вывод в формате сообщений об ошибках, принятых в Emacs

  • xdocs - вывод в формате sdocs для дальнейшей обработки в Maven

Пример:
<findbugs output="xml:withMessages">

Нет
outputFileЭтот атрибут определяет имя файла отчета.

Пример:
<findbugs outputFile="${reports.file}">

Нет
jvmargsЭтот атрибут определяет параметры, передаваемые JVM. Чаще всего для анализа проектов следует увеличить память, доступную JVM. Делается это с помощью параметра -Xmx, пример:
<findbugs jvmargs="-Xmx512M">

Нет
failOnErrorЭтот логический атрибут определяет, следует ли останавливать процесс сборки, если в при проверке обнаружены ошибки. Принимает значения true/false, по умолчанию false
Нет
errorPropertyЭтот атрибут определяет имя свойства, которое будет установлено в true, если при проверке будут обнаружены ошибки. Пример:

<findbugs errorProperty="errors.present">

Нет
warningsPropertyЭтот атрибут определяет имя свойства, которое будет установлено в true, если при проверке будут сгенерированы предупреждения. Пример:

<findbugs warningsProperty="warnings.present">

Нет


Остальные параметры можно посмотреть здесь.

Теперь напишем цель для файла сборки, которая будет проверять наш (а может быть, и не наш) код на наличие ошибок.


<target name="findbugs" depends="compile">
<findbugs home="${findbugs.home.dir}" output="xml:withMessages" outputFile="${home.dir}/findbugs.xml" jvmargs="-Xmx512M">
<sourcePath path="${src.dir}" />
<class location="${build.dir}"/>
<auxClasspath refid="main.classpath"/>
</findbugs>
</target>


После прогона FindBugs по коду одного из проектов (который писал не я =))) ) командой

> ant findbugs

были получены, например, следующие сообщения (всего получено сообщений 261):

Class defines clone() but doesn't implement Cloneable
May expose internal representation by returning reference to mutable object
Comparison of String objects using == or !=
Class defines compareTo(...) and uses Object.equals()
int division result cast to double or float
clone method does not call super.clone()
Method concatenates strings using + in a loop
Unread field

+ куча сообщений по поводу неосовобождения ресурсов после их использования (незакрытые java.sql.PreparedStatement, java.sql.ResultSet и т.п.).

FindBugs имеет удобный графический интерфейс для просмотра файла отчета. На Windows для запуска GUI можно запустить файл FINDBUGS_HOME\bin\findbugs.bat, или выполнить команду

java [JVM arguments] -jar $FINDBUGS_HOME/lib/findbugs.jar options...


Просмотр ошибок в GUI выглядит следующим образом:



В общем, после такой проверки, я предложил использовать FindBugs в нашем стандартном цикле разработки на каждой девелоперской машине. Каждый девелопер перед коммитом своих изменений в SVN обязан проверить свой код с помощью FindBugs и исправить конструкции в коде, на которые FindBugs ругается. Мое предложение было принято, и я ожидаю, что качество кода в наших проектах повысится благодаря использованию этого инструмента.

PhDComics Desktop

Классный Desktop от PhDComics:


Особенно забавно: Papers you have been meaning to read for months )))
To Do, To Really Do )))

Это сообщение, конечно, не по теме блога, но все же такой LOL я не мог не записать =)

Ooops, ссылку забыл =) Кликать сюда

понедельник, 15 июня 2009 г.

Получение простых чисел (prime numbers) в SQL

Просто забавная задачка на SQL: дана табличка с единственным полем (тип INT), содержащее числа от 1 до (допустим) 100. Требуется одним SQL-запросом вывести все простые числа из этой таблицы. Решение приведено для MySQL. Адаптировать его для других СУБД - без проблем.

Скрипт,создающий и заполняющий таблицу:




DROP TABLE IF EXISTS pnum;
CREATE TABLE pnum (
num int(11) NOT NULL
) ENGINE=HEAP;

DROP PROCEDURE IF EXISTS InsertNumbers;

DELIMITER |

CREATE PROCEDURE InsertNumbers(IN LMT INT)
BEGIN
DECLARE CNT INT;
SET CNT = 1;
WHILE CNT <= LMT DO
INSERT INTO pnum VALUES (CNT);
SET CNT = CNT + 1;
END WHILE;
END;

DELIMITER ;

CALL InsertNumbers(100);



Запрос, выводящий все простые числа:




SELECT p1.num FROM pnum p1
WHERE NOT EXISTS
(SELECT p2.num FROM pnum p2
WHERE p2.num < p1.num AND p2.num > 1 AND p1.num % p2.num = 0);

среда, 10 июня 2009 г.

Получение Revision Number из SVN с помощью Ant

Несколько часов назад у меня появилась задача: в веб-интерфейсе разрабатываемой системы необходимо показывать номер ревизии (Revision Number) из Subversion. Все исходники проекта хранятся в Subversion, поэтому перед компиляцией (или перед сборкой) необходимо запрашивать Subversion на получение Revision Number проекта, и его вставлять в интерфейс. Естественно, это должно выполняться автоматически перед каждой сборкой.

Решено сделать следующее:
1) на место, куда требуется вставлять Revision Number (страничка head.jsp, которая вставляется инклюдом во все остальные странички), вставляется лексема __REVISION_NUMBER__, которая перед компиляцией заменяется на актуальный Revision Number, полученный из Subversion
2) создается цель (target) в Ant, которая получает Revision Number из SVN, и сохраняет его в соответствующее свойство (property)
3) применяется команда <replace>, которая заменяет лексему __REVISION_NUMBER__ на значение свойства, куда сохранили Revision Number

Все стандартно, но встает вопрос - как получить Revision Number из Subversion. Немножко погуглив, я нашел решение, про которое и расскажу.

В Subversion есть команда info, которая выдает информацию о локальном либо удаленном (находящимся в репозитории) файле. Выдается следующая информация:

  • Path
  • Name
  • URL
  • Revision
  • Repository Root
  • Repository UUID
  • Node Kind
  • Last Changed Author
  • Last Changed Revision
  • Last Changed Date
  • Text Last Updated
  • Properties Last Updated
  • Checksum
  • Lock Token
  • Lock Owner
  • Lock Creation Date


Например, при выполнении команды

svn info build.xml

на консоль будет выведена следующая информация:

Path: build.xml
Name: build.xml
URL: svn://svn/PROJECT/trunk
Repository Root: svn://svn
Repository UUID: (UUID репозитория)
Revision: 3558
Node Kind: file
Schedule: normal
Last Changed Author: awm
Last Changed Rev: 3443
Last Changed Date: (date goes here...)
Text Last Updated: (date goes here...)
Checksum: (checksum goes here....)


Для нас здесь важно свойство Revision. Именно его значение мы и собираемся вставлять вместо лексемы __REVISION_NUMBER__. Причем не важно, какой именно файл будет передан в качестве аргумента команде svn info. Дело в том, что номер ревизии является общим для всех файлом в проекте, поэтому выполнить команду svn info мы можем для любого файле проекта, в частности, и для файла сборки build.xml.

Удобно и то, что формат представления информации командой svn info понимается командами Ant, в частности, командой <property file=""/> и <loadproperties/>. Это означает, что скинув дамп вывода в какой-нибудь файл, мы затем можем без изменений передать этой файл этим командам, и они прочитают свойства, которые выведены командой svn info:
Name, URL, Repository Root, Repository UUID, Revision, Node Kind, Schedule, Last Changed, Author, Last Changed Rev, Last Changed Date, Text Last Updated, Checksum.

Была написана следующая цель в файле сборки:



<target name="get.revision.and.replace" description="Get revision number, and places it into JSPs">
<exec executable="svn" output="svndump">
<arg value="info"/>
<!-- any filename can be here, because we need only Revision property, which is general for all filenames -->
<arg value="build.xml"/>
</exec>
<loadproperties srcfile="svndump"/>
<echo message="current revision is ${Revision}"/>
<delete file="svndump"/>
<replace file="${basedir}/project/WebContent/templates/head.jsp" token="__REVISION_NUMBER__" value="${Revision}"/>
<replace file="${basedir}/project/WebContent/templates/head_login.jsp" token="__REVISION_NUMBER__" value="${Revision}"/>
</target>


Атрибут output команды <exec> определяет файл, куда записывается вывод исполняемой команды. Как уже говорилось, формат этого вывода таков (Имя: значение), что понимается командами загрузки свойств Ant без изменений, что очень удобно. После загрузки свойств командой <loadproperties srcfile="svndump"/> файл вывода svndump удаляется командой <delete file="svndump"/>. В конце концов, два раза вызывается команда <replace>, которая и заменяет в определенных файлах требуемую лексему на номер ревизии.

пятница, 5 июня 2009 г.

Требования к Senior Java Developer

Нашел здесь требования к Java Senior Developer. Выкладываю их к себе в блог, как ориентир на будущее.

Senior Java Developer

Job Description:

Responsible for Java/J2EE/J2SE application development supporting business objectives while providing expertise in software development lifecycle phases from concept and design to testing. Analyzes, designs and builds component-based applications in a Web/internet delivery environment, including
- introduction of an application layer,
- modeling techniques,
- component and object-oriented design,
- complex algorithmic coding,
- systematic approaches to application integration.

Works on new and existing applications along with enhancements web sites, web applications, and infrastructure. Performs hands-on coding, mentors junior developers and assists in architecting web content solutions. Serves as liaison to internal customers, research groups and various business support areas. Expertise in server-side web presentation technologies.

Desirable Experience:

• A Bachelor’s degree in Computer Science, Mathematics or related discipline is required, or equivalent work experience and technical training.

• 5 or more years of Java experience managing application design, software development, maintenance and delivery with at least 2 years with application servers; e.g. Oracle 8i/9i/10g/11i Application Server; SQL Server; J2EE Application Server; IBM Websphere, Tomcat, or BEA Weblogic; Apache.
Related Articles

• Minimum of 3 years technical work experience in a team environment as a programmer.

• Strong experience with Web architecture and Web design and development tools and languages, e.g. JSP, JDBC, Java Servlets, Javabeans/EJBs, ASP, VB Script, SQL, Perl, UNIX, C++, CORBA, JavaScript, SOAP, XML/XSLT/FOP, HTML, Struts, ANT, CVS, CMP, WebLogic.

• Strong relational database experience (DB2, Sybase, SQL, Oracle 8i/9i/10g/11i). Strong background working in distributed Unix environments.

• Experience designing N-tiered applications, with multi-tier architecture and production Internet architectures.

• Strong experience developing and implementing highly complex technical solutions in a J2EE web environment.

• Superior organizational and time management skills.

• Excellent written and verbal communication skills

• Highly desirable:
- Rapid applications development or extreme programming experience
- Sun Certified Developer for Java 2 Platform
- Sun Certified Programmer for Java 2 Platform
- Sun Certified Developer for Java Web Services
- JavaScript Development Certified Professional
- Java Development Certified Professional

четверг, 4 июня 2009 г.

Использование фильтров в Apache Ant - ClassConstants

Apache Ant используется для компиляции, сборки и развертывания проектов на Java. Ant является стандартом де-факто в мире Java. Он предоставляет множество возможностей, одна из которых - это фильтрация (преобразование) содержимого обрабатываемых файлов. В данном сообщении рассмотрено использование фильтров в Ant.

Представьте, что при выполнении процесса сборки вашего приложения вам необходимо обработать некоторые текстовые файлы. Например, в одном файле необходимо заменить некоторые сочетания символов (tokens) на другие, а в другой файл должно быть записано содержимое нескольких файлом, т.е. произведена конкатенация файлов. В этом случае необходимо использовать фильтры Ant. Фильтры используются для обработки содержимого файлов при их копировании, перемещении. Работать с фильтрами могут следующие команды (tasks):

<сoncat>
<copy>
<loadfile>
<loadproperties>
<move>


Фильтры могут объединяться в цепочки фильтров (FilterChains), таким образом позволяя применить к обрабатываемому файлу несколько фильтров в определенной последовательности. Такой механизм является аналогом Unix pipes.

Рассмотрим имеющиеся фильтры:

ClassConstants

Выводит все константы (поля, объявленные final) Java-класса. Модификатор доступа не влияет на вывод. Рассмотрим пример: вывод констант класса в текстовый файл.

Класс следующий:

TestClassConstants.java:


package ru.testant;

import java.util.ArrayList;

public class TestClassConstants {

public final int A = 1;
protected final double B = 0.123;
private final String C = "test string";
private final ArrayList D = new ArrayList();
int E = 2;
}


после этого создадим следующую структуру каталогов (PROJECT_HOME - корневой каталог проекта):
PROJECT_HOME/build/classes
PROJECT_HOME/src/ru/testant


В каталог PROJECT_HOME/src/ru/testant поместим наш файл TestClassConstants.java

Затем создадим следующий файл сборки


build.xml:


<project name="ant-filters1" default="testcc" basedir=".">
<description>
Test ClassConstants Ant filter
</description>

<property name="src.dir" value="src"/>
<property name="build.dir" value="build/classes"/>

<target name="compile" description="Compile sources">
<javac srcdir="${src.dir}" destdir="${build.dir}"/>
</target>

<target name="testcc" description="Test ClassConstants filter" depends="compile">
<copy file="${build.dir}/ru/testant/TestClassConstants.class" encoding="ISO-8859-1" tofile="11.txt">
<filterchain>
<classconstants/>
</filterchain>
</copy>
</target>
</project>


и поместим его в корневой каталог проекта (т.е. PROJECT_HOME). Основное внимание надо обратить на цель testcc. В ней происходит копирование обработанного контента из файла TestClassConstants.class в файл 11.txt, который будет размещен в PROJECT_HOME. Кодировка ISO-8859-1 должна использоваться в этом фильтре, т.к. при ее использовании не происходит потери информации при перекодировании characters --> int и обратно.

Фильтр ClassConstants использует библиотеку Apache BCEL (Byte Code Engineering Library), которую надо скачать с сайта http://jakarta.apache.org/bcel и поместить в каталог ANT_HOME/lib (где ANT_HOME - каталог, куда установлен ANT). Этот каталог не надо указывать в classpath, поскольку JARы из него добавляются в classpath автоматически перед началом работы Ant.

В итоге, наш проект включает в себя следующие файлы:
  • PROJECT_HOME/build.xml

  • PROJECT_HOME/src/ru/testant/TestClassConstants.java

  • ANT_HOME/lib/bcel-5.2.jar


Затем вызывается цель testcc:

> ant testcc

Получившийся файл 11.txt имеет следующий вид:


11.txt:

A=1
B=0.123
C=test string


Видно, что константа D не сохранена в файл, поскольку она не является примитивным типом, а также не сохранено неконстантное поле E.

Если фильтр применить внутри команды <loadproperties>, то тогда константы класса будут загружены в виде свойств, где имя константы - будет именем свойства, а значение константы - значением свойства. Пример:


<loadproperties srcfile="${build.dir}/ru/testant/TestClassConstants.class" encoding="ISO-8859-1">
<filterchain>
<classconstants/>
</filterchain>
</loadproperties>

Постоянные читатели