понедельник, 6 декабря 2010 г.

Java Generics vs Наследование

Вообщем по просьбе Viktor Davion объясняю (в 140 твиттеровских символов не уложился), отчасти, что побудило к посту ненависти. Под катом, т.к. содержит плоды работы воспалённого мозга.

пятница, 5 ноября 2010 г.

Code coverage для интеграционного тестирования

Итак, ситуация следующая. Есть у нас Integration Test Framework, написанный специально для нашего приложения. Но начался он писаться задолго после старта работы над приложением, поэтому накопилось большое количество кода, который тестируется в лучшем случае unit тестами. Как это всё вместе работает не совсем понятно :) Для это писался этот ITF, но он ещё в разработке и тесты пишутся достаточно медленно. Вот в процессе обсуждение этого факта, мне подумалось, а почему бы не считать каково покрытие тестами у нас. Вообще, было замечено, что многие программисты очень плохо относятся ко всяким таким характеристикам как сode coverage. Вот для unit тестов оно у нас не считается, но локально иногда запускаешь посмотреть, сейчас у нас больше 80%. Но, как мне сказали, о чём это говорит? Из за неправильного ответа на этот вопрос и возникает такая не любовь к этой величине. То, что сode coverage 100% это не значит, что вы достигли просветление, ваше приложение идеально и вы на пути к нирване. Но это говорит о том, что весь написанный код, действительно вызывается, т.е. это немного спасает от простых ошибок.

Но вернёмся к ITF. Почему я считаю, что для интеграционных тестов сode coverage более полезен.

  1. Меньше возможностей для искусственной манипуляции результатами. Как иногда бывает с простыми численными характеристиками, за что их и не любят, люди увлекаются попыткой их достижения. И если мы говорим о unit тестах, то достаточно просто написать много, по своей сути бесполезных тестов, которые дадут требуемую цифру. Это ещё одна из причин, почему test first :) Но для интеграционных тестов это не так. Они не работают напрямую с методами или классами. Они используют исключительно пользовательское api, создание файла, нажатие на кнопку и т.п. Поэтому пытаться искусственно делать 100% покрытие очень трудоёмкая задача. Кроме того, даже для ручного тестирования, многие все равно прикручивают сode coverage, чтобы увидеть это заветное число, после того как тестировщики погоняют приложение. Это даёт много интересной информации для размышления.
  2. Мы оказались в ситуации, что интеграционные тесты приходится писать вдогонку. Уже есть большая часть приложения, команда разрослась почти до 10 человек и всего 2 тестировщика. Таким образом, отслеживая величину покрытия, можно делать выводы о том, мы догоняем или всё таки уже опоздали. Т.е. если на предыдущей недели было 20%, а на этой стало 23%, то мы на верном пути, но если стало 10%, то слишком увлеклись генерированием не проверенного кода и наверно стоит поднапрячь программистов, чтобы они побольше внимания уделяли написанию интеграционных тестов в помощь тестировщикам.

В результате всё таки создал такой таск и решил на досуге посмотреть, что может предложить open source java community поэтому поводу. Надо была попроще библиотека, которая позволяла посчитать покрытие кода, по результатам работы приложения. начал конечно же с Open Source Software in Java. В принципе, предложения есть. Я выбрал последнее с "оригинальным" названием CodeCover. Последняя версия можно считать 1.0.* оформленная ажно в 2009 году, но плагин под эклипс обновлялся в 2010, да и остальные тулы, тоже не сильно новее. Маленький проектик hoto можно увидеть под катом, если заинтересовало.

суббота, 30 октября 2010 г.

Субботний вечер пропаганды маргинальных технологий: hgsubversion

Как можно было заметить, обычно в блоге пишу про рабочие моменты. Поэтому решил отдельно выделить записи, которые будут посвященных не очень популярных в "энтерпрайз" кругах, технологиях и темах :)

Сегодня хотел немного затронуть тему распределенных систем контроля версий. На самом деле, эта тема достаточно популярно, просто больших компаниях приживается очень плохо, некоторые, как я узнал, например в IBA, вообще cvs пользуются. Подробно рассказывать именно про DVCS не думаю, что есть смысл, материалов на всех языках мира хватает, как в общем про DVCS, так и про конкретные системы. Один из главных зачинатилей всего безобразия, это Линус, так что в качестве введения можно посмотреть его выступление в кампусе Google, где в его духе эмоционально рассказано кто не прав и почему :)

Я расскажу про немного другую проблему. Когда вы делаете не свой продукт, а под заказ или являетесь всего лишь небольшим подпроектом огромного проекта, часть технологий вы выбрать не можете и вынужденны использовать subversion. Но ведь хочется вкусненького, и это возможно. Почти все разработчики DVCS понимают, что все сейчас прям не бросятся переводить свои репозитории на новые рельсы. Поэтому присутствует интеграция с многими другими системами контроля версий. Например с subversion. Вот хотел бы рассказать про hgsubversion, проект, который позволяет интегрировать subversion и mercurial репозитории. Был опробован на реальном проекте и пока никаких проблем не возникло.

пятница, 29 октября 2010 г.

Требую больше immutability!

Вообщем тривиальная слоган, его можно прочитать даже в достаточно старой Joshua Bloch: Effective Java, Item 15: Minimize mutability.

Под катом чутка банальности, так как сегодня доделал и наболело.

пятница, 22 октября 2010 г.

Java сompile time annotations

Ох.. тут давече затеяли большой рефакторинг проекта в рамках которого я решил сделать пару классов immutable. Казалось бы, делаем все поля private + final и всё ок. Но нет, для ссылочных типов это не работает. Так как в этом случае мы не можем изменить ссылку на этот объект, которая храниться в поле, но за то можем изменить сам этот объект, например добавить в коллекцию ещё пару объектов. Тут есть два подхода, оба приводят к равно ценном результату (immutable классу):

  1. Требовать, чтобы все ссылочные типы также были immutable, как String например. Для коллекций можно использовать Collections.unmodifiableCollection метод.
  2. Второй способ заключается в основе ООП: инкапсуляции. Ни один метод не возвращает объект по ссылке, только его копию, например вместо возврата коллекции можно вернуть её неизменяемую копию с помощью описанного выше метода.
Вообщем то всё достаточно очевидно. Ввиду ряда особенностей и уменьшения последствий рефаторинга, посоветовавшись, я решил воспользоваться вторым способом. Но пока рассматривал первый метод, захотелось аннотацию, которая бы сигнализировала, что объект не изменяем и корректность класса проверялась на этапе компиляции. Если кого-то заинтересовала, краткое описание под катом.

четверг, 16 сентября 2010 г.

Log4j file header

Давно не писал, но тут накипело. Читал буквально позавчера статью Модульный дизайн, или «что такое DIP, SRP, IoC, DI и т.п.». Вот там говориться про великий Log4j, какой он распрекрасный. И вот понадобилось мне сделать header для файлов. Такой вот конфиг:
  1. <appender name="eventhistoryfile" class=" org.apache.log4j.RollingFileAppender">  
  2.     <param name="LogFileName" value="${log4j.file.name}"/>  
  3.   
  4.     <layout class="org.apache.log4j.PatternLayout">  
  5.         <param name="ConversionPattern" value="${log4j.pattern}"/>  
  6.     </layout>  
  7. </appender>  
Очень простой.. Как добавить хедер? Оказывается PatternLayout содержит пустую реализацию метода getHeader. Ну и как это называется??? Ладно, сделал свой, который добавляет эту функциональность, не сложно. Идём дальше. Запустил, работает. Вырубил приложение, запустил ещё раз появился ещё один header в файле О_о я нахожусь в шоке. Пришлось ещё наследоваться от RollingFileAppender и переопределять у него метод writeHeader, чтобы header в файл писался один раз (код взят в исходном FileAppender  только добавлена последняя проверка).
  1. @Override  
  2. protected void writeHeader() {  
  3.     if (this.layout == null) {  
  4.         return;  
  5.     }  
  6.     String header = layout.getHeader();  
  7.     if (header == null && this.qw == null) {  
  8.         return;  
  9.     }  
  10.     File f = new File(this.getFile());  
  11.     if (!f.exists() || (f.exists() && f.length() == 0)) {  
  12.         this.qw.write(header);  
  13.     }  
  14. }  
Вообщем, негодую.

вторник, 22 июня 2010 г.

Crash Racing

Для получения Диплома осталось только подписать обходной лист. Вот разбирал архивы, чтобы всё накопленное записать на dvd и сдать в архив, и наткнулся на прикольный проект. На втором курсе у нас Java вёл очень хороший преподаватель Гафуров Сергей Владимирович. И в конце курса  мы разбились на команды и где-то недели две писали сетевую игру на Java. Мы выбрали гоночки и как видно из заголовка поста - разрушительные гоночки. Это был невероятный проект и очень полезный, заставил по другому взглянуть на программирование. Всё таки работа в команде, это работа в команде :) Где-то в середине проекта стало понятно, что мы не успеваем и пришлось перебороть юношеский максимализм и волевым усилием отказаться от оружия. Зато к сроку у нас был работающий проект, хоть и разрушать ничего нельзя было. Вот на память записал видео сражения 4х ботов на моей любимой карте, за ними наблюдать иногда веселее чем самому играть, а благодаря Диме Гончарову выиграть у них практически не возможно =DD это удавалось только Диме Хоревичу. Кстати да, о лицах :) Первый Дима как не сложно догадаться работал над физикой и ИИ, второй над UI, Катя Цвирко сделала потрясающие карты и работала с панелью игрового поля. А я скромно писал сетевое взаимодействие. Играть можно было по сети. Так как ботов было трудно победить - сражались между собой :) Игра не поражает воображение, но это всё таки учебный проект второкурсников, причём в жатые сроки, мне нравиться ^__^



Код не покажу, стыдно :)

Upd: Видео уже лень делать, вот скриншоты :)

воскресенье, 16 мая 2010 г.

Прикольный workaround для деплоя репортов в Reporting Services

Reporting Services штука полузная. Сделаные в VS репорты для него можно деплоить по разному. Через web интерфейс, с помощью SQL Server Management Studio. Но иногда нужны автоматические скрипты. Для этого есть утилитка rs.exe.
rs.exe -i MyReportScript.rss -s http://localhost/reportserver
MyReportScript.rss - это скрипт на VB, который и загружает репорт или репорты.
Вот так примерно выглядит код загрузки:

  1. Try
  2.   Dim stream As FileStream = File.OpenRead(reportName + ".rdl")
  3.   definition = New [Byte](stream.Length) {}
  4.   stream.Read(definition, 0, CInt(stream.Length))
  5.   stream.Close()
  6.   Catch e As IOException
  7.   Console.WriteLine (e.Message)
  8. End Try
  9.  
  10. Try
  11.   warnings = rs.CreateReport(reportName, reportsPath, True, definition, props)
  12.   Console.WriteLine("Upload Report: {0}.", reportName)
  13.   Catch e As Exception
  14.   Console.WriteLine (e.Message)
  15. End Try

Репорты делал не я и делались они 2005 студии. У меня стоит 2008, после пары изменений, я получаю вот такое сообщение:
System.Web.Services.Protocols.SoapException: The report definition is not valid.  Details: '.', hexadecimal value 0x00, is an invalid character. Line 4334, position 10.
Через Management Studio загружается нормально.
Конечно же файл репорта представляет из себя просто ужасный xml. И что мы видем в строчке 4334? Заключительный закрывающийся тэг:
Становится совсем не понятно, но всемогущий гугль легко находит линку:
RS.EXE fails to deploy a report upgraded from SQL Server 2005 Reporting Services - "report definition is not valid"
Предложенное решение проблемы меня просто поставило в тупик

  1. Replacing
  2.      My_Byte_Array = New [Byte](stream.Length) {}
  3. With
  4.      My_Byte_Array = New [Byte](stream.Length - 1) {}

Вообщем даже не представляю как это у них получилось cгенерировать такую проблему в новой студии, вообщем просто в шоке =D
Хотя я иногда и по круче отжиги делаю :)

З.Ы.: Сорри за не самую красивую подсветку, просто мой любимый Online syntax highlighter что-то с VB на отрез отказался работать, а HTML Source Code Syntax Highlighter хоть и подсвечивает код, но не очень красиво.

З.Ы.Ы.: Катя подсказала более крутой сервис для подсветки кода, так как ей нужен был Object C, то она нашла вот этот, количество языков поражает. Только надо выбрать опцию Combine Style and HTML, чтобы он не генерировал отдельно css стиль, а всё выдавал html кодом. Качество подсветки хорошее.

воскресенье, 18 апреля 2010 г.

Java - отличная платформа, но слишком неповоротливый язык.

Хотел поделится порцией очевидностей. Java - это действительно отличная платформа для разработки, с больших комьюнити, огромным числом Open Source библиотек, инструментов и т.п. Но вот язык Java этой платформы слишком устарел. Лично я не поклонник нагромождения синтаксического сахара, как например в Ruby. Но я сравниваю с C# и там есть много действительно полезных плюшек, практически полезных (я вообще пишу только о том, с чем лично столкнулся).
Вот два небольших примера.
1. Есть очень популярная задача, организовать конвейер для обработки документов, которые по сути из себя представляют Map или, если говорим о C#, Dictionary. К Dictionary в дот нете, у меня есть одна большая претензия, он слишком часто кидает исключения. Но речь не об этом. Вот так выглядит простой документ, я добавил в него метод AddProperty, с помощью которого можно не только добавить элемент, но и перезаписать старый:
  1. public class Document : Dictionary<stringobject>  
  2.     {  
  3.         public Document AddProperty(string name, object value)  
  4.         {  
  5.             if (ContainsKey(name))  
  6.                 Remove(name);  
  7.   
  8.             Add(name, value);  
  9.             return this;  
  10.         }  
  11.     }  
Собственно ничего интересно, за исключение полезной штучки, метод возвращает не void, а самого себя.
API конвейера тоже простое:
  1. public class Pipeline  
  2. {  
  3.     public void AddProccessor(Func<Document, Document> proccessor)  
  4.     {  
  5.     }  
  6.   
  7.     public void Execute()  
  8.     {  
  9.     }  
  10. }  
Вот тут почти самый главный момент. Делегаты это хорошо, мне надоели в том же свинге листенеры с одним методом, это ужас какой-то.
Но самое интересное теперь. Иногда требуется добавить очень простую обработку, которая просто добавляет пару полей к документу. В java пришлось бы поступить как в свинге, анонимный класс с реализацией метода интерфейса листенера. Но в C# коллега подсказал очень и очень красивое решение:
  1. var pipeline = new Pipeline();  
  2. pipeline.AddProccessor(record => record.AddProperty("NewProp""Value").AddProperty("AnotherProp", 1));  
Действительно в одну строчку.
2. На эту тему копий сломано не мало, перегрузка методов или параметры по умолчанию. Кто бы, что ни говорил, но C# 4.0 c параметрами по умолчанию плюс именование параметров, лично для меня, отличное, красивое и удобно решение.
Вот есть метод:
  1. public static string format(string value,   
  2.             bool option1 = falsebool option2 = false)  
  3. {  
  4.     return value;  
  5. }  
Параметры по умолчанию позволяют не забивать и так не маленькие исходные тексты кучей не нужного текста. Но к сожалению они не идеальны без одной маленькой штучки. Вот скажите, что надо сделать, чтобы вызвать описанный метод, но чтобы option2 был true. Придется или перегружать метод, или при вызове передавать false для option1. Но проблема решена в C# 4.0. При вызове метода передаваемые параметры можно именовать, так что теперь можно не беспокоиться об очерёдности параметров, а также решить указанную выше проблему простым способом:
  1. format("test", option2: true);  
Да, для JVM есть много интересных альтернативных языков, например мне нравится Clojure. Но например у него есть проблемы с производительности и вообще, его активно портируют на CLR. Хотелось бы, чтобы и основной язык платформы более активно развивался.

Ну и так, ещё одна рабочая мысль за сегодня: парное программирование - это не только весело, но и действительно продуктивно.

И совсем напоследок цитата дня (которую правда уже постил в Buzz'е, но уж больно понравилась):
  1. select fun, profit from real_world where relational=false  

понедельник, 5 апреля 2010 г.

Java Decompiler

Да, по названию поста будет понятно о чём речь :)
По старой традиции использовал для этих целей всегда JAD. Но он уже достаточно давно заброшен и не справляется с некоторыми фишками. Нашёл хорошую альтернативу, так прям и называется Java Decompiler. Работает как отдельное приложение, так и как плагин к эклипсу.
Больше всего не давали жить две штуки (исходники, которые мне нужны, были просто напичканы ими): ассерты и дженерики. Вот на составил небольшой демонстрационный примерчик:
Исходный текст:

  1. public class Example {  
  2.       
  3.     public class Doing<T extends Some> extends Base<T> {  
  4.         @Override  
  5.         public T doSomething(T param) {  
  6.             assert(param == null);  
  7.             return param;  
  8.         }  
  9.     }  
  10.   
  11.     public abstract class Base<T extends Some> {  
  12.         public abstract T doSomething(T param);  
  13.     }  
  14.   
  15.     public class Some {  
  16.         public Some doing() {  
  17.             return this;  
  18.         }  
  19.     }  
  20.       
  21. }  
То что выдал JAD:
  1. // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.  
  2. // Jad home page: http://www.kpdus.com/jad.html  
  3. // Decompiler options: packimports(3)   
  4. // Source File Name:   Example.java  
  5.   
  6. public class Example {  
  7.     public abstract class Base {  
  8.   
  9.         public abstract Some doSomething(Some some);  
  10.   
  11.         final Example this$0;  
  12.   
  13.         public Base() {  
  14.             this$0 = Example.this;  
  15.             super();  
  16.         }  
  17.     }  
  18.   
  19.     public class Doing extends Base {  
  20.   
  21.         public Some doSomething(Some param) {  
  22.             if (!$assertionsDisabled && param != null)  
  23.                 throw new AssertionError();  
  24.             else  
  25.                 return param;  
  26.         }  
  27.   
  28.         final Example this$0;  
  29.         static final boolean $assertionsDisabled = !Example  
  30.                 .desiredAssertionStatus();  
  31.   
  32.         public Doing() {  
  33.             this$0 = Example.this;  
  34.             super();  
  35.         }  
  36.     }  
  37.   
  38.     public class Some {  
  39.   
  40.         public Some doing() {  
  41.             return this;  
  42.         }  
  43.   
  44.         final Example this$0;  
  45.   
  46.         public Some() {  
  47.             this$0 = Example.this;  
  48.             super();  
  49.         }  
  50.     }  
  51.   
  52.     public Example() {  
  53.     }  
  54. }  
И наш чемпион Java Decompiler:
  1. public class Example {  
  2.     public abstract class Base<T extends Example.Some> {  
  3.         public Base() {  
  4.         }  
  5.   
  6.         public abstract T doSomething(T paramT);  
  7.     }  
  8.   
  9.     public class Doing<T extends Example.Some> extends Example.Base<T> {  
  10.         public Doing() {  
  11.             super(Example.this);  
  12.         }  
  13.   
  14.         public T doSomething(T param) {  
  15.             assert (param == null);  
  16.             return param;  
  17.         }  
  18.     }  
  19.   
  20.     public class Some {  
  21.         public Some() {  
  22.         }  
  23.   
  24.         public Some doing() {  
  25.             return this;  
  26.         }  
  27.     }  
  28. }  
Разница заметна на лицо, комментарии излишни :)

Подсветка кода

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

Единственный недостаток, с <, > вообщем запрещёнными символами, которые, например, встречаются в LINQ выражениях. Придётся самостоятельно делать эксейпинг.

Да, и ещё одним преимуществом такого статического подхода, является корректное отображения кода в RSS ридерах и пр. трансляторах, js они не могу проинтерпретировать, а со статичным html'ем справляются на ура.

UPDATE: Не знаю как просмотрел, но есть отличная js библиотечка для динамической подсветки highlight.js от Ивана Сагалаева. Поддерживает целых 35 языков и всё время пополняется, кроме того, у неё есть отличный набор из более чем десятка тем. Я себе под новый дизайн выбрал Dark.

<?xml version="1.0"?>
<response value="ok" xml:lang="en">
  <text>Ok</text>
  <comment html_allowed="true"/>
  <ns1:description><![CDATA[
  CDATA is <not> magical.
  ]]></ns1:description>
  <a></a> <a/>
</response>

понедельник, 22 марта 2010 г.

Ant Magic

Совсем недавно на проекте столкнулись с забавным поведением компиляции из анта. Решил описать, так как по моему это некорректное поведение анта. Вот так выглядит проект:
Код простой. Класс 1 вызывает метод класса 2.
  1. import test.sub2.Sub2;  
  2.   
  3. public class Sub1 {  
  4.    
  5.  public static void sub1() {  
  6.   Sub2.sub2();  
  7.  }  
  8.    
  9. }  
  10.   
  11. package test.sub2;  
  12.   
  13. public class Sub2 {  
  14.  public static void sub2() {  
  15.  }  
  16. }  
Класс Sub2 из другого модуля и не должен компилироваться с основным. Класс 1 тоже не должен компилировать с основным, они оба из модуля 2, но лежат в разных пакетах и пакет Sub1 по случайности не был исключён из компиляции основного модуля 1. Т.о. ант скрипт выглядит вот так:
  1. <project name="TestProject" default="compile">  
  2.   
  3.  <target name="compile">  
  4.   <delete>  
  5.    <fileset dir="bin">  
  6.     <include name="**/*.class"/>  
  7.    </fileset>  
  8.   </delete>  
  9.   <javac srcdir="src" destdir="bin">  
  10.    <exclude name="test/sub2/**" />  
  11.   </javac>  
  12.  </target>  
  13.   
  14. </project>  
Чего я ожидал, что класс Sub2 не будет компилироваться, так как я его исключил и будет ошибка компиляции так как Sub1 пытается импортировать класс, которого нет в classpath.
Но не тут то было, его тоже скомпилирует и всё пройдёт саксес. Хорошо тут специально симулированный простой случай, но начинается полная (_!_) когда Sub2 вызывает ещё какой-то класс, в котором есть специфические методы из библиотек, которых нет в classpath модуля1 и соответственно это умный ант разрулить не может. И человек рвёт на себе волосы, потому что по сути он всё сделал правильно, модуль 2 компилируется без проблем, какого падает компиляция модуля 1, хотя в его классах не было изменений, не понятно. Вообщем вот он какой magic.