Tagged: java

toString() contract

Today I read great article of Fabian Kessler Java toString(): the Program Logic vs. Debug Dilemma.

In short, it is not obvious how to override method and what exactly means «string representation of object»:

Hrm. So there are mainly 2 uses:
String representation: toString() returns the object’s value «as string» as close as possible.
It is absolutely required to override toString(), and to do it in this way.

Debug information: the object’s values for the human.
For example IntelliJ IDEA’s default toString() template generates this kind.
It’s just nice to have.

JavaDoc says:

Returns a string representation of the object. In general, the
toString method returns a string that
«textually represents» this object. The result should
be a concise but informative representation that is easy for a
person to read.
It is recommended that all subclasses override this method.

Quotation from the «Effective Java» book:

One important decision you’ll have to make when implementing a toString
method is whether to specify the format of the return value in the documentation.
It is recommended that you do this for value classes, such as phone numbers or
matrices. The advantage of specifying the format is that it serves as a standard,
unambiguous, human-readable representation of the object. This representation
can be used for input and output and in persistent human-readable data objects,
such as XML documents. If you specify the format, it’s usually a good idea to pro-
vide a matching static factory or constructor so programmers can easily translate
back and forth between the object and its string representation. This approach is
taken by many value classes in the Java platform libraries, including BigInteger ,
BigDecimal , and most of the boxed primitive classes.

I found that most of code that I saw uses some unwritten «toString() contact». The contract mostly based on difference between Entity and Value objects.

1. «String typization» mechanism

The Delphi programming language has a great conception of Variant type. It is difficult to me to explain this in few words. It is like an universal container for the primitive types.

This is like a def keyword in Groovy: it mean any type. But any primitive type, not an basic Object.
And there is convention like a «Groovy Truth» how one primitive type should be converted to another.

Unfortunately Java hasn’t Variant type, so it uses String as it replacement. Hence, any primitive type you can convert to sting presentation: numbers, dates and boolean.
This is called StringlyTyped pattern and actually it makes code understanding painfully.

String typization

That’s why we should look on toString() as on data converter «as string».
And it should work only with Value classes and primitive datatypes wrappers, i.e. Integer, Boolean, Double, Date, Phone Numbers etc.

Important thing here is that this resulting string representation of value can be parsed verse to get an original value.

2. Serialization

In more common sense toString() it is a kind of serialization.
Even more, toString() can return JSON on CSV value.
But you should remember that deserialization is not granted by method contract.
That’s why you should use standard approach and implement Serializable interface.

But you still can use toString() as serialization for basic types that already contains written toString(), parse() and valueOf() methods.

3. Human readable representation of object

Another thing is when we need to show the object to a user. In this case it may be better to create another method called like a getDisplayName(), getTitle() or getCaption().

For example User class can contain getFullName() that return First Name and Second Name with space between them.

public class User {
   String firstName;
   String lastName;
   
   String getFullName() {
       return firstName + " " + lastName;
   }
}

Why it is better? First of all human readable names can be different and one string representation method may be not enough.

For example lets take a look on Facebook. In the private mail conversation between two users we can just write first name. At the public profile page we can display «First name + (nickname) Second name».

Also, usually this kind of display names can be localized. For example name of month or book title.

4. Log/Debug representation

In all other cases toString() used for logging or debug output. Just to make developer life easier.

The Groovy has a helpful @ToString annotation that generates this method in runtime.
For example class User can contain toString() method that return user login or email:

@ToString(includeNames = true, includes = 'email,firstName')
class User {
   String email
   String firstName
}

...

def user = new User(email: "admin@example.com", firstName: "Administrator")
assert user.toString() == "User(email:admin@example.com, firstName:Administrator)"

...
log.info("User ${user} logged in")

Here log.info() will call toString() method that is not good. I’m totally agree with Fabian and such method should be named toDebug() or dump(), but not toString().

But you should always remember that logging can cause problems for security and performance.
Not always you need to write to log everything that object contains.
For example if a hacker got an access to logs he can see a lot of stuff like user password and other credentials or even credit card number.
So you should take care to exclude from logs sensitive information.
Always mask credit card numbers and users personal info.

Another big problem is log injection vulnerability when some XSS scripts from logs can be executed in dashboard. Please read carefully OWASP Logging Cheat Sheet

Conclusion

Using this conventions may make your code better and safer.
I would be very thankful if you share conventions from your experience 🙂

Проблемы OpenJDK или в очередной раз про идиотизм Orcale

Ну для начала лекция для тех кому интересно въехать в курс дела:

У меня к OpenJDK есть такие претензии:
1. Название, я поначалу подумал «ну вот ето JDK а мне на сервер достаточно только JRE» и искал OpenJRE 🙂
2. Сайт: посмотрите на сайт Mono http://www.mono-project.com/ — красивый, всё ясно куда нажимать. А теперь посмотрите на это уродство http://openjdk.java.net/ . Я не дизайнер, и неприхотливый но даже меня тошнит. А ведь это же маркетинг!
4. Почему IDEA упорно не дружит с ним? Это меня настораживает — вы говорите что OpenJDK на 99% соответсвует обычной OracleJDK, но идея пишет что будут проблемы с графикой. Это правда? В чём причина? Напишите JetBrains чтобы они убрали это сообщение.
5. Где версия под Windows?
6. Отправлять патчик по мылу это прошлый век — перенесите проект на GitHub!

До сих пор у всех осталось отношение к OpenJDK как к кастрированому форку православной сановской Явы.
Боюсь чтоы избавится от этого его прийдётся переименовать и впихнуть в него что-то модное чтобы пресса рассказала о нём как о прорыве.

Обратите внимание что я говорю только о маркетиге, дитрибуции и организации проекта а не о технических деталях. В той или иной степени такие же проблемы у всех остальных проетков Оракла.

Ruby, Ruby, Ruby, Ruby! And do ya, do ya, do ya, do ya!

Просто несколько вещей которые бесят постоянно. Можно расписывать их много, но тут я акцентирую внимание на том что уровень вхождения в Яву выходит большим из-за банального разгильдяйства Oracle. Неожиданно, но они совсем не учитывают что платформу нужно ПРОДАВАТЬ, и очень важен маркетинг. А ведь ещё «корпорацией бабла» называются 🙂

JDK, JRE — WTF?

JDK содержит в себе полную JRE + SDK (компилятор, декомпилятор, дебагер и.т.д). Тем не менее, вместо того чтобы ставить отдельно SDK, всегда приходится полностью выкачивать JDK содержащий JRE.
Самое интересное, что теперь у нас есть OpenJDK, и несмотря на JDK в названии он же и JRE. Я окончательно запутался.

Is Java open soure?

Принято считать, что Java открытая платформа. Что можно считать открытой платформой? Сам язык Java, насколько я в курсе это вполне проприетарная технология. Открытые исходные коды — это ещё не значит что это открытая платформа. Например разработка Android ведётся в строжайшем секрете и ты не знаешь чем тебя удивит Google в следующей версии. Но так-то исходники открывают, да.
Изначально SUN не хотела выкладывать исходники Java из-за того что те содержали какой-то патентированый код. Я не очень понимаю почему это было большой проблемой. Можно было выложить то, что можно, а запатентированный код либо переписать, либо выделить в отдельный модуль. В принципе так вроде и сделали в результате чего получилась OpenJDK. Но тут есть ключевой момент: OpenJDK — это отдельный проект. Его нужно отдельно расскручивать, у него отдельный сайт, своё сообщество и, что потенциально опасно, свой код который может начать отличатся от того что внутри оракловой JDK. И тут сразу возникает следующий вопрос…

OpenJDK == Oracle?

На конференциях парни из Oracle утверждают что OpenJDK это на 99.9% тоже что и Oracle JRE, и что они вообще билдятся из одних исходников. Тем не менее, IntelliJ упорно отказывается работать с OpenJDK и запугивают проблемами с производительностью. Наверное тут дело в криворукости JetBrains, но всё равно настораживает.
Тем не менее, все продолжают использовать именно OracleJDK

Oracle принципиально не делает дистрибутива для Debian, а только для своей Solaris.
А в Ubuntu выпилили этот пакет, в результате каждый раз установка джавы превращаются в пляску с бубном: нужно прописать сторонний PPA, в котором инсталятор Java, который за тебя выкачивает её из сайта Oracle и устанавливает. Естественно такой пакет из стороннего PPA нельзя использовать как зависимость в своём пакете.

  1. На сайте Оракла чёрт ногу сломит, всё страшное и некрасивое. Ещё и вымагают регистрацию. Никакой заботы о пользователях, никакого маркетинга.
  2. Почему-то инсталятор сам не прописывает переменную JAVA_HOME и JDK_HOME. Что ему, жалко что-ли? Вообще переменные окружения это большое зло, может уже пора искоренить их вовсе?
  3. Успех платформы во многом зависит от наличия пакетного менеджера в поставке с ней. В Ruby есть gem, в NodeJS — npm. В яве ничего.
  4. Туториалы на оф. сайте стары как мир, без подсветки синтаксиса и никакого интерактива.

Как так получилось что откровенно поганая платформа Ruby стала такой модной и популярной? Умелый маркетинг.
Посмотрите на сайт например Mono, он выглядит приятней и опрятней, пускай это аутсайдерский проект, но даже там пытаются хоть ка кто для людей сделать.
Я уже молчу про прямого конкурента Java — платформу .NET.
На сайте много всячины и сразу же баннер на Visual Studio.
А теперь гляньте на Web 1.0 сайт явы. Фу!
Кстати, тоже самое касается самого языка программирования.
Если MS в целях маркетинга впихнул всё нужное и ненужное в C#, то в Яве даже самые нужные фичи добавляют с огромным скрипом. Вот например, лямбд все ждут уже десятилетие, и вот уже наконец в восьмой Яве будут.
Конечно, Ява по философии консервативна и стремится быть минималистичной, но не до такой же степени чтобы ждать пятой версии для for each циклов.

Когда то ява была прорывом, и очень потенциальной технологией.
Но тянули до последнего с открытием исходников, забили на дистрибьюцию, забили на десктопы, оконные программы смотрятся ужасно, просрали рынок аплетов который теперь заполонил поганый Flash, поругались с MS, а теперь ещё и с Google.

Маркетинг господа. Если вы ещё не поняли, то в постиндустриальной эпохе это самое главное.

Software Environments

Программа может работать в разных условиях (environments), например у программиста на компьютере, где Windows и пару гибибайт оперативки, или на реальном «производственном» (production) сервере, с настоящей БД, с миллионом пользователей, мощным железом и под каким нибудь FreeBSD.
Соответсвенно на уровне конфигурации и кода нужно учитывать такие разные Environments.
Например если произошла ошибка то на компьютере программиста (development environment) мы вываливаем ему весь stacktrace, а вот на работающем сайте (production) мы пишем все ошибки только в логи, а перед пользователями извеняемся.

Так вот, какие чаще всего нужны Environments?
К сожалению я нигде в интернете не нашёл хорошего объяснения какие environments нужны и как их конфигурировать. Очень много и хорошо описано в документации к Grails но там описанные только самые базовые случаи.
Ещё немного описано в Википедии.
Поэтому постараюсь описать их все исходя из моего опыта.

Development, dev

Компьютер программиста
БД создаётся in-memory и каждый раз удаляется при выключении программы.
Пример конфигурации:

development {
    dataSource {
        url = 'jdbc:h2:mem:devDb' // Драйвер БД: H2, база в памяти, называние БД devDb
        dbCreate = 'create'       // Создать схему БД автоматически
        loggingSql = true         // Логировать SQL запросы
    }
    log4j = {
        all 'grails.app'  // Логируем всё из пакетов нашего приложения           
        root {
            all 'stdout'  // Выводим только в консоль
        }
    }
}

Здесь опция dbCreate = ‘create’ указывает что БД будет каждый раз снова создаваться снова.
прямо в оперативной памяти. При выходе из программы она будет уничтожатся.

More on dbCreate
Hibernate can automatically create the database tables required for your domain model. You have some control over when and how it does this through the dbCreate property, which can take these values:
create — Drops the existing schemaCreates the schema on startup, dropping existing tables, indexes, etc. first.
create-drop — Same as create, but also drops the tables when the application shuts down cleanly.
update — Creates missing tables and indexes, and updates the current schema without dropping any tables or data. Note that this can’t properly handle many schema changes like column renames (you’re left with the old column containing the existing data).
validate — Makes no changes to your database. Compares the configuration with the existing database schema and reports warnings.
Можно играться этими опциями.
Например если вы не хотите чтобы данные очищались, а просто обновилась схема то ставьте update и сохраняйте БД в файле.
Если у вас уже production БД то ставьте только validate а саму миграцию БД делайте с помощью SQL скриптов через инструменты DBMaintain или LiquidBase.

Test

Для прогона автоматических тестов.
Тут стоит отметить что тесты бывают:

  • модульные (unit) — им не нужно никаких конфигураций Environments по определению
  • интеграционные (integration, i11n) — вот для них обычно уже нужна БД а то и полностью стартовать сервер.
  • функциональные (functional) — это когда имитируются действия пользователя, и проверяется что все кнопочки работают как должны.

Т.е. environment нужен для integration и functional и в идеале разный.

Production, prod

Настоящий сайт.
Тут стоит отметить что здесь нельзя хранить настоящие пароли от БД. Я бы рекомендовал просто указать имя DSN, например для JNDI или ODBC.
Логирование нужно настроить в файл и на email. А вот в консоль логировать бессмысленно.

production {
    grails.serverURL = 'http://greenpay.com/'
    portMapper.httpPort = 80
    portMapper.httpsPort = 443
    dataSource {
        jndiName = "java:comp/env/myDataSource"
    }
    log4j = {
        warn 'grails.app'
        root {
            warn 'main', 'smtp'
            additivity = true
        }
    }
}

Staging, stg

Это тестовый сервер с железом, конфигурацией и данными максимально приближёнными к production.
Обычно данные берутся скриптом с реального сервера, и только персональная информация (пароли, email) перетираются тестовыми в целях безопасности.
На staging проводят нагрузочное тестирование и проверяются что реальные данные не поломают приложение. Например может оказатся что у вас в реальной БД есть пользователь с именем длинней чем размер поля в новой версии БД.

QA

Сборка программы для QA инженеров (тестировщиков).
Не путать с test environment — то для автоматических тестов. Автотесты не могут поймать все дефекты, особенно связанные с внешним видом.
Тут можно выделить два случая QA…

QA dev

Обычно собирается автоматически каждую ночь (или после обеда), после чего тестировщики проверяют выполненные вчера программистом задачи.

QA release

За пару недель перед релизом новая разработка замораживается (code freeze) или уже ведётся в других ветках кода для следующего релиза.
За это время тестировщики делают регрессионное тестирование где проверяют что старый функционал работает как и прежде и ничего не поломалось.

HEAD

Continuous Integration, CI — это сервер который автоматически собирает ваш проект после каждого изменения кода (pull). HEAD — это последняя версия кода, т.е.
head environment это версия приложения максимально соответствующая последнему состоянию кода.
Зачем это нужно? Ну если у вас в команде несколько человек то бывает удобно когда есть общий доступный сервер на котором быстренько можно что-то глянуть.
Т.е. похоже на QA но обновляется чаще и имеет право содержать ошибки.

Demo, Acceptance

Демо версия для сдачи итерации заказчику. Этот сервер должен быть доступен заказчику из интернета.
Всё должно быть сделано так чтобы не произошло «демо эффекта» когда ты показываешь заказчику новый функционал и вдруг он выдаёт ошибку.
Поэтому БД всегда создаётся новая. Презентационные данные должны быть красивыми и заранее оттестированными и отрепетированными.
Известные баги нужно обходить стороной. Заказчику главное увидеть как оно работает, а то что там ещё есть баги это не так важно. Важными они станут только в ветке QA release.

Это самые базовые случаи разных сред и конфигураций которые я видел. Разумеется их может быть больше. На это влияет структура проекта, процесс разработки и особенности серверов.
А вообще это вроде как относится к понятию Configuration Managment, но как я вижу на практике этим термином просто называют инструменты Chef и Puppet которые хранят конфигурацию как код.

Буду рад услышать если вы можете дополнить эту информацию или подскажете где можно почитать.
Расскажите как у вас настроено на проекте 🙂 Спасибо.

UPD Также почитайте мой совет Избегайте использования Environment вне файлов конфигураций

[Перевод] Пол Грэм: Парадокс Питона (The Python Paradox)

Автор: Пол Грэм, August 2004
Python + Java
В недавней беседе я сказал то, что расстроило большое количество людей: «Вы можете найти более сообразительных программистов для работы над проектом на Python’е, чем для работы над Java проектом».

Я не имел в виду, что программисты на Java тупые. Я имел в виду, что программисты на Python’е сообразительнее. Ведь это огромная работа выучить новый язык программирования. Люди учат Python не потому, что он даст им возможность получить работу. Эти люди учат новый язык, потому что они искренне любят программировать и не удовлетворены теми языками, которые уже знают.

Это делает их как раз теми, кого компании по разработке ПО следует хотеть нанять. Именно поэтому, из-за отсутствия лучшего названия, я назову это «парадоксом Python’а»: если компания хочет написать своё ПО на относительно эзотерическом языке, то она наймет лучших программистов, потому что она привлечёт только тех, кто позаботился выучить его. Для программистов парадокс можно перефразировать так: язык, который нужно выучить, чтобы получить хорошую работу, это тот язык, который люди учат не только чтобы получить работу.

Не много компаний достаточно мудры, чтобы осознать это. Но и здесь происходить выбор: это как раз те компании, в которых программисты захотят работать. Например, Google. Когда они нанимают java программистов, они также хотят увидеть опыт программирования на Python.

Мой друг, который знает большинство распространенных языков, использует Python для большинства своих проектов. Он говорит, что основная причина, это то как выглядит исходный код. Это может показаться несерьезной причиной выбора языка. Но это намного вачнее, чем кажется: когда ты пишешь программу, ты тратишь больше времени на чтение, чем на написание кода. Ты добавляешь куски исходного кода также, как скульптор добавляет куски глины. Язык, который делает исходный код уродливым, сводит с ума придирчивого программиста, как глина с комьями скульптора.

При упоминании уродливого кода, люди конечно подумают о Perl. Но внешнее уродство Perl не из этого разряда. Настоящее уродство не грубо выглядящий синтаксис, а неизбежность построения программ на не правильных концепциях. Perl может выглядеть как нелепый персонаж мультфильма, но в некоторых случаях он концептуально превосходит Python.

До сих пор, не смотря ни на что, оба языка являются «движущимися мишенями». Тем не менее их объединяет с Ruby (и Icon, и J, и Lisp, и Smalltalk) тот факт, что они создавались и использовались людьми, которые действительно интересуются программированием. И тот велик, кто делает это хорошо.

Оригинал (англ.) The Python Paradox
Статья хоть старая но до сих пор актуальная. Перевод не мой, я просто делаю репост чтобы не потерялся текст.
И кстати да, не дайте себе запудрить мозги, Ява лучше 😉
joseph-ducreux-meme-disregard-python-acquire-java-080b2b

Код Java в bat-файле

Маленькая забавность о том, как использовать все возможности Java в обычных bat-скриптах. Или сделать скриптовый язык из Java.

Далее рассказан простой способ скомпилировать и запустить Java код, который находится внутри bat-файла.

Навеяно статьёй Код С# в bat-файле. Я реализовал тоже самое для Java. Только я не стал делать поиск папки в которую установлен JDK, т.е. в переменной %PATH% должен быть указан путь к %JDK_HOME%/bin

Не буду долго томить, вот код bat файла Test.bat:

/*
copy %0 .\%~n0.java
javac %~n0.java
cls
java %~n0
del %~n0.class
del %~n0.java
exit
*/

public class Test {
 public static void main(String[] args) throws Exception {
  System.out.println("Hello world from Java!");
  System.out.println("Press Enter to exit");
  System.in.read();
 }
}

Выполнив полученный батник увидим:

Работает это так:

  1. Сначала работает bat-интерпретатор. Первую строчку скрипта (комментарий Java /*) он не понимает и выдает ошибку. Но не страшно, это чисто визуальное неудобство.
  2. Затем мы копируем бат файл в файл с таким же именем но уже с расширением *.java, иначе компилятор javac откажется его компилировать. Выражение %~n0 вернёт имя файла без расширения: параметр 0 всегда имя файла а квалификатор n только имя файла. Следите за названием файла, поскольку имя *.java файла должно соответствовать имени класса внутри него.
  3. Далее компилируем консольным компилятором javac который в качестве аргумента принимает путь к файлу с исходником. В нашем случае исходник находится в теле bat-файла (строки 10–18). Текст bat-команд игнорируется, как комментарий (строки 1 и 9).
  4. После работы компилятора, получаем class файл, который запускаем (предварительно подчистив экран командой cls) и удаляем после завершения его работы.
  5. Работа bat-интерпретатора заканчивается на 8 строке (exit).

Если есть трудности с синтаксисом bat скриптов могу посоветовать хорошую статью.

Это был пример, я использую ещё немного улучшенный вариант bat файла. В случае ошибок компиляции он их выведет. Ещё также он добавит в CLASSPATH все *.jar файлы которые он найдёт рядом.

/*
@echo off
copy %0 .\%~n0.java
cls
javac -classpath CLASSPATH=.\*.jar;.\lib\*.jar  %~n0.java
if %ERRORLEVEL% equ 0 (
 cls
 java %~n0
 del %~n0.class
)
del %~n0.java
exit
*/

public class Test {
 public static void main(String[] args) throws Exception {
  System.out.println("Hello world from Java!");
  System.out.println("Press Enter to exit...");
  System.in.read();
 }
}

Целесообразность, скорее just for fun. Но иногда бывает удобнее выслать новичку один файл, уже с прописанной строчкой компиляции, который нужно просто выполнить.

UPD

Узнал о библиотеке Launch4j которая может превратить ваш jar файл в обычный запускаемый exe