ToStringBuilder and multi-thread environment

Just found some article about using Apache Commons' ToStringBuilder tool and about what a wonderful tool it is. And I decided to write about it too.

Well, there were few projects where I was able to use Apache Commons Lang tool called ToStringBuilder. It's very useful tool that helps to print information about class instance in the toString() method. I just was need to write

@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("Id", id)
.append("Name", name)
.append("Description", description)
.toString();
}

and as result got a string with nice-look instance description. It was perfect for me.
But now I'm not using this tool. And wasn't using it for the last 10 months as well. I would be happy to use it rather than own implementation over StringBuilder but I can't.
ToStringBuilder has problems with running in multi-thread environment. If you have class A that uses ToStringBuilder and you're going to use instances of this class in different threads - be careful!

Google Books

English version by Google Translate

Не секрет, що у світі Google Books відбуваються чималі зміни.
Про це все розказує сам Google.

Сервіс дуже цікавий, і сподіваюся кількість повністю доступних та щойно виданих книхо з часом збільшиться. Адже є книжки, яких вже і не купиш, а є книжки - як хочеться купити або навіть потрібно купити, але змушений часто це робити без перегляду хоч якогось контенту. Гугл може дати можливість віртуального швидкого перегляду книжки перед купівлею, - наче ви в справжньому магазині.

Подобається також можливість створювати власну бібліотеку. Або вставляти фрейв сторінку із книжкою :)

Maven допоможе!

Вирішив записати декілька корисних речей, з якими доводиться мати часто справу користувачу мавена в повсякденному житті, наприклад, мені:

  1. Запуск із ключиком -o каже мавену працювати в оффлайновому режимі, а це інколи економить час якого немає. В цьому випадку мавен шукає всі бібліотеки та плагіни в локальному репозиторії і навіть не пробує стукатися до центральних репозиторіїв за апдейтом тощо. Також корисно з повільним та нестабільним інтернетом.

  2. Проперті maven.test.skip поможе виконати компіляцію та збірку проекту без виконання тестів. Корисно, коли середовище для тестування не готово або ж з тестами все ок, а проблема з якимись плагінами або просто треба зібрати проект, щоб викласти десь. Може запускатися таким чином: mvn install -Dmaven.test.skip=true.

  3. Файл MVN_HOME/conf/settings.xml містить ряд корисних настройок. Однією з них є localRepository, за допомогою якої можна вказати шлях до репозиторія десь на диску відмінному від C:. Користувачі windows часто форматують C: і рідко бекаплять репозиторій. Правильний шлях може зберегти чимало часу при перевстановленні windows наступного разу.

  4. Якщо використовується плагін мавена для генерації javadoc для проекта з декількома модулями, то може бути дуже корисно виконати агрегації згенерованої документації, а для цього можна скористатися властивістю aggregate, наприклад:

    <plugin>
      <artifactId>maven-javadoc-plugin>/artifactId>
       <configuration>
        <aggregate>true</aggregate>
       </configuration>
    </plugin>


Список не повний, буду його розширювати як тільки буду згадувати та знаходити нові тіпси.

A Few Maven Tips



Decided to write some useful things, which often helps in everyday life to Maven user, like me:

  1. The command line argument -o said to Maven to work in offline mode and this sometimes saves time. In this case, maven is lookіng for all the libraries and plugins in the local repository and do not even tries to ping central repositories for update. Also useful with slow and unstable Internet connection.

  2. Property maven.test.skip helps to perform project compilation without running test cases. Could be useful when the environment is not yet ready for testing or all tests green. Run command in next format: mvn install -Dmaven.test.skip=true.

  3. File MVN_HOME/conf/settings.xml contains a number of useful settings. One of them is localRepository, through which you can specify the repository somewhere on the disk D: etc. Windows users oftenly format drive C: and rarely backups local Maven repository. Correct value for this setting can save a lot of time after re-installing windows next time.

  4. If you are using Maven plugin to generate Javadoc for a project with multiple modules, it can be very useful to perform the aggregation of generated documentation. In this case use property aggregate, for example:

    <plugin>
      <artifactId>maven-javadoc-plugin>/artifactId>
       <configuration>
        <aggregate>true</aggregate>
       </configuration>
    </plugin>


The list is not complete, it will expand as soon as I remember or find new tips.

Динамічний репорт за допомогою JasperReports

На проекті, над яким я працював десь рік тому, постала задача виконувати динамічну генерацію друкованої версії звіту. На проекті для генерації звітів використовувався JasperReports.
Отже, постали наступні питання
1. Як виконувати динамічну генерацію звіту:
- чи є для цього API,
- чи все-таки генерувати XML, а потім його компілювати?
2. Як передавати дані?

При виборі способу генерації динамічного звіту я вирішив використати перший варіант - працювати із API JasperReports для формування представлення звіту. Ідея генерувати XML в коді мені не дуже подобалася, тому що в цьому випадку код виглядав для мене заплутаним. Крім того, зміна версії бібліотеки могла привести до помилки, яку би було тяжко визначити. З API ситуація була інакшою: досягалася непогана чистота коду із можливістю абстрагування для різних видів звітів. Та й при компіляції можна було визначити сумісність використовуваного API із версією бібліотеки.

Дивно, але на той час мене чомусь не осягнула ідея використовувати JSP, Velocity чи FreeMarker для генерації шаблону звіту. Мені здається, що на даний момент я би використав саме цей спосіб.


Тут дуже знадобився такий клас як JasperDesign та набір класів сімейства Design. До класів сімейства Design відносяться:
  • JRDesignField
  • JRDesignStyle
  • JRDesignStaticText
  • JRDesignTextField
  • JRDesignExpression
  • JRDesignBand
  • і багато інших.
Перелічені вище класи є найбільш використовуваними. І якщо ви працювали колись із JasperReports, створюючи звіти за допомогою JRXML файла, то вам можуть бути знайомі такі елементи, як field, style, staticText, textField, band тощо. Суть використання Design класів полягає в тому, що вони представляють собою ті ж самі елементи JRXML. Обєктна модель справді дуже проста і зручна в розумінні.
Створювати репорти динамічно просто і зручно. Але це є виключною ситуацією, оскільки завжди можна скористатися вашим олюбленим редактором XML або ж iReport.

Мені свого часу довелося створити чимало репортів за допомогою Jasper. Більшість шаблонів створювалися в редакторі XML, чимало динамічних створювалися в коді і могли поєднювати ще декілька динамічних репортів, інші просто накидувалися швидкоруч в iReport.

Для тих, кому цікаво, хочу привести декілька прикладів використання API JasperReport.

Створення елементів звіту:

public static JRStyle getDefaultStyle() {
JRDesignStyle style = new JRDesignStyle();
style.setName("defaultStyle");
style.setDefault(true);
style.setFontName("Arial");
style.setFontSize(8);
style.setPdfFontName("/TTF/arial.ttf");
style.setPdfEncoding("Identity-H");
style.setPdfEmbedded(true);
return style;
}

public static JRStyle getHeadStyle() {
JRDesignStyle style = new JRDesignStyle();
style.setName("headStyle");
style.setDefault(true);
style.setFontName("Arial");
style.setFontSize(8);
style.setBold(true);
style.setPdfFontName("/TTF/arial.ttf");
style.setPdfEncoding("Identity-H");
style.setPdfEmbedded(true);
return style;
}

public static JRDesignElement staticTextElement(String text,
int height, int width,
int x, int y,
boolean headStyle) {
JRDesignStaticText staticText = new JRDesignStaticText();
staticText.setText(text);
staticText.setWidth(width);
staticText.setHeight(height);
staticText.setX(x);
staticText.setY(y);
staticText.setBorder(JRGraphicElement.PEN_THIN);
if (headStyle) staticText.setStyle(getHeadStyle());

return staticText;
}

public static JRDesignElement textFieldElement(String text, Class type,
int height, int width,
int x, int y,
boolean headStyle) {
JRDesignTextField textField = new JRDesignTextField();
textField.setWidth(width);
textField.setHeight(height);
textField.setX(x);
textField.setY(y);
textField.setBorder(JRGraphicElement.PEN_THIN);
if (headStyle) textField.setStyle(getHeadStyle());

JRDesignExpression expression = new JRDesignExpression();
expression.setText(text);
expression.setValueClassName(type.getName());
textField.setExpression(expression);

return textField;
}

public static JRDesignElement textFieldElement(Property property,
int height, int width,
int x, int y,
boolean headStyle) {
JRDesignTextField textField = new JRDesignTextField();
textField.setWidth(width);
textField.setHeight(height);
textField.setX(x);
textField.setY(y);
textField.setBorder(JRGraphicElement.PEN_THIN);
if (headStyle) textField.setStyle(getHeadStyle());

JRDesignExpression expression = new JRDesignExpression();
expression.setValueClassName(property.getType().getName());
if (property instanceof Column) {
expression.addFieldChunk(property.getName());
}
else {
expression.addParameterChunk(property.getName());
}
textField.setExpression(expression);

return textField;
}


Ініціалізація звіту:

JasperDesign reportDesign = new JasperDesign();
reportDesign.setName(context.getDataResult().getDataReport().reportType().getReportName());
reportDesign.setOrientation(JasperDesign.ORIENTATION_LANDSCAPE);
reportDesign.setIgnorePagination(true);
reportDesign.setTopMargin(5);
reportDesign.setBottomMargin(5);
reportDesign.setLeftMargin(5);
reportDesign.setRightMargin(5);
reportDesign.setWhenNoDataType(JasperDesign.WHEN_NO_DATA_TYPE_NO_PAGES);

reportDesign.addImport("java.lang.*");
reportDesign.addImport("java.math.*");
reportDesign.addImport("java.util.*");
reportDesign.addImport("net.sf.jasperreports.engine.*");
reportDesign.addImport("net.sf.jasperreports.engine.data.*");

reportDesign.addStyle(getDefaultStyle());
reportDesign.addStyle(getHeadStyle());


Заповнення звіту:

JRDesignBand rootBand = new JRDesignBand();
rootBand.setHeight(40);
rootBand.setSplitAllowed(true);
reportDesign.setDetail(rootBand); // could be setTitle() or setSummary() etc.

rootBand.addElement(textFieldElement("Details", String.class,
height, 120, currentX, currentY, false));



Компіляція звіту:

JasperReport compileReport(JasperDesign reportDesign) throws ReportException {
try {
log.info("Compiling report...");
return JasperCompileManager.compileReport(reportDesign);
}
catch (Exception e) {
log.error("Error to compile report, cause:", e);
throw new ReportException("Error to compile report!", e);
}
}


Використання отриманого JasperReport:

JasperDesign reportDesign = new JasperDesign();

//
// preparing and filling report design goes here...
//

JasperReport jasperReport = compileReport(jasperDesign);
JasperPrint jasperPrint = JasperFillManager.fillReport(
jasperReport, prepareParameters(), prepareDataSource());