Translate

четвер, 13 серпня 2015 р.

АОР & Performance tool


Що спільного в словах AOP та Performance tool ? -нічого , окрім того що задопомогою АОР ми будемо пиляти власного "валосипеда" і спробуємо ним поміря швидкодію Java аплікацій.

Отож, визначимо цілі нашого проекту :

  • Отримаувати заміри виконання методі задопомогою анотацій
  • Керувати історією викликів мотода
  • Отримувати результат в наступних форматах : String, JSON, HTML
Проект унас буде збиратися Apache Maven'ом, для імплементації АОР використаємо AspectJ а тести напишемо на JUnit.


Отже, створюємо наш проект командою : "mvn archetype:create -DgroupId=org.ar.stat4j  -DartifactId=Stat4J" з цього бидно архітектуру пакетів а також горду назву "Stat4J" (типу заявка на світове визнання 8-D ).

Додаємо залежності в pom.xml :

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${aspectj.version}</version>
  </dependency>
</dependencies>


Створюємо пакет "org.ar.stat4j.annotations" і додаємо в в нього клас "Stat4JPoint.java", це і буде наша анотація котру ставитимемо над методом котрий будемо заміряти. Відкриваємо клас і пишемо в ньому настйпний код :

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Stat4JPoint {
}



@Target - місце над яким можна буде декларувати дану анотацію , у нашому випадку це методи.
@Retention - як довго дана анотація буде зберігатися над анотованим типом, у нашому випадку напротязі усього виконання аплікації.
@interface - вказує що даний клас є анотацією

Отже тепер ми маємо анотацію яка буде вказувати на методи котрі нам треба заміряти, залишилось тільки відслідковувати коли під час виконання аплікацію будуть викликатися анотовані цією анотацією методи. Для цього нам і потрібне АОР.

Створюємо пакет "org.ar.stat4j.aspects" і додаємо туди наш аспект "Stat4JAsp.aj". Даний аспект повинен спрацьовувати коли буде викликано будь який метод анотований нашою анотацією. Для цього в середену аспекту пишемо :

@Aspectpublic class Stat4JAsp 

 @Around("execution(* *(..)) &&@annotation(org.ar.stat4j.annotations.Stat4JPoint)")  public Object around(ProceedingJoinPoint point) throws Throwable {

  } 

}

@Aspect - вказує AspectJ плагіну (який ми пізніше додато до конфігурації мавена) , що даний клас є аспектом.
@Around - вказує що даний аспект має виконатись навколо методу , тобто не перед чи після а саме навко методу (іншими словами його обгортаємо) а як аргумент ми передаємо умову при якій даний аспект повинен виконатись : * - будь який ретурн тип , * - будь яка назва методу , (..) - будь яка кількість аргументів,  && - а також , @annotation(<type>) - анотація типу <type>.

Тепер у нас є анотація і аспект який її відслідковуватиме.

Залишилось лише додати плагін до bild секції в pom.xml який під час компіляції проекту зробить всю ту "магію" яка називається АОР (Обгорне анотовані методи своєю іпмлементацією). Плагін має виглядати так :

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>aspectj-maven-plugin</artifactId>
  <version>${aj.mvn.pg.version}</version>
  <executions>
    <execution>
      <goals>
        <goal>compile</goal><!-- to weave all your main classes -->
        <goal>test-compile</goal><!-- to weave all your test classes --> 
      </goals>
    </execution>
</executions>
<configuration>
  <sources>
    <source>
      <basedir>src/main/java/org/ar/stat4j/aspects</basedir>
      <includes>
        <include>**/*.aj</include>
      </includes>
    </source>
  </sources>
  <complianceLevel>1.8</complianceLevel>
  <outxml>false</outxml>
  <verbose>true</verbose>
  <showWeaveInfo>true</showWeaveInfo>
  <source>1.8</source>
  <target>1.8</target>
</configuration></plugin>

В даній конфігурації важливо вірно вказати пакет в якому знаходяться ваші аспекти а також версію джави (у нашому випадку 1.8).

Пробуємо білдати наш проект : mvn clean install , і якщо бачимо :

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

тоді усе гаразд. 

Тепер давайте зробимо власне нашу утиліту яка буде відповідати за початок заміру, кінець а також вивід статистики. Створюємо в пакеті "org.ar.stat4j" класс Stat4J.java і зробимо його синглтоном щоб там де він буде використовуватись він завжди був єдиним на всю систему.

public class Stat4J { 
  private static Stat4J instance;
  private Stat4J() {} 
  public static Stat4J instance() { 
    if (instance == null) { 
      synchronized (Stat4J.class) { 
        if (instance == null) { 
          instance = new Stat4J();
        } 
      } 
    } 
    return instance;
  }
}


Тепер, маючи головний клас нашої утиліти створюємо пакет "org.ar.atat4j.data" і додаємо два класи , перший для зберігання усієї статистики , а другий для зберігання статистики конкретно по одному виклику метода :

Statistic.java

public class Statistic { 
  private List<Point> stats = new ArrayList<>();
  ...
}

Point.java

public class Point implements Comparable<Point>{ 
  public static final int NANO_IN_MILIS = 1000000;
  private long startTrack;
  private long finishTrack;
  ...
}


Тепер навчимо клас Statistic.java обраховувати інформацію про свої заміри :

(приклад наводжу на одному з методів, повна версія буде доступна за посилання в кінці поста)

public long getMaxExecutionTimeInNano(){ 
  if(!stats.isEmpty()) { 
    Collections.sort(stats);
    return stats.get(stats.size()-1).executionTimeInNanoseconds();
  } 
  return 0;
}


Маючи об'єкт статистики і всю інформацію про виклики методу додамо мапу в головний об'єкт нашої утиліти котра за іменем класу зберігатиме іншу мапу , яка в свою чергу за іменем методу зберігатиме об'єкт статистики :

Stat4J.java

...
private Map<String, Map<String, Statistic>> records;
...

Також додаємо метод який розпочинатиме та закічнуватиме заміри :

public Point startTrack(String componentName, String pointName) { 
  Point point = new Point(System.nanoTime());
  if (!records.containsKey(componentName)) { 
    Statistic statistic = new Statistic();
    statistic.getPoints().add(point);
    Map<String, Statistic> pointToStat = new HashMap<>();
    pointToStat.put(pointName, statistic);
    records.put(componentName, pointToStat);
  } else if (!records.get(componentName).containsKey(pointName)) {
    Statistic statistic = new Statistic();
    statistic.getPoints().add(point);
    records.get(componentName).put(pointName, statistic);
  } else {
    records.get(componentName).get(pointName).getPoints().add(point);
  }
  return point;
}
Перший if ініціалізує цілу групу замірів відштовхуєчись від імені класу. Другий if інішіалізує групу замірів на базі методів у випадку якщо група класу вже ініціалізована. А третій if спрацює якщо група класу і методі уже є , тоді тільки додасть новий замір.


public void stopTrack(Point point) { 
  point.finish(System.nanoTime());
}


Для зупинки заміру просто проставляємо поточну дату (час закінчення) в точку заміру .

Маючи все вище написане залишаться оновити наш аспект і в його тілі вказати що перш ніж виконати метод ми повинні розпочати замір а далі виконати метод і після цього закінчити замір:

Point statisticPoint =   Stat4J.instance().startTrack(point.getTarget().getClass().getCanonicalName(), MethodSignature.class.cast(point.getSignature()).getMethod().getName());

Object result = point.proceed();
Stat4J.instance().stopTrack(statisticPoint);

return result;

У випадку якщо наш метод нічого не повертає , ми всеодно повинні повертати Object як результат виконання методу так як на даному етапі аспект нічого про сам метод не знає.


Компілюємо аплікацію і впевнюємось що унас все зроблено вірно.

Залишилось вивестинаш результат. Для цього створюємо пакет "org.ar.stat4j.printers" і додаємо в нього клас "StringPriner,java". Він повинен приймати наші об"єкти статистики та примати аргументом параметр що буде вказувати треба нам історія викликів методу чи ні . 

Ось код цього прінтера : 

public class StringPrinter implements Printer{

  public static final String COMPONENT_OUTPUT = "%1s.%2s:\tCall times: %3s,\tMax:     %4s (ns)\t|\t%5s(ms),\tMin: %6s(ns)\t|\t%7s(ms),\tAvr: %8s(ns)\t|\t%9s(ms);\n";

  public static final String POINT_OUTPUT = "\t\t%1s:\tExecution date:  
  %2s,\tExecution time: %3s\t(ns)|\t%4s(ms);\n";

  public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM.dd.yy   HH:mm:ss.SSS");

  @Override
  public String print(Map<String, Map<String, Statistic>> statistic, boolean history){
    final StringBuilder strBld = new StringBuilder();

    statistic.forEach((componentName, points) -> points.forEach((pointName, stats) 
    -> strBld.append(generateComponentStatistic(componentName, pointName, stats,
    history))));

   return strBld.toString();
  } 

  private String generateComponentStatistic(String componentName, String pointName,   Statistic statistic, boolean history) {

    StringBuilder strBld = new StringBuilder(); 
    strBld.append(String.format(COMPONENT_OUTPUT, componentName, pointName,
    statistic.getPointSize(), statistic.getMaxExecutionTimeInNano(),
    statistic.getMaxExecutionTimeInMili(),
    statistic.getMinExecutionTimeInNano(),
    statistic.getMinExecutionTimeInMili(),
    statistic.getAverageExecutionTimeInNano(),
    statistic.getAverageExecutionTimeInMili()));
    if (history && statistic.getPoints().size() > 1) { 
      statistic.getPoints().forEach((statPoint) ->
      strBld.append(String.format(POINT_OUTPUT,pointName,
      DATE_FORMAT.format(statPoint.getExecutionDate()),
      statPoint.executionTimeInNanoseconds(),
      statPoint.executionTimeInMiliseconds())));
    } 
    return strBld.toString(); 
  } 
}
Тепер потрібно додати метод в головний клас утиліти для виводу статистики який буде приймати аргументом тип прінтера (String, JSON, HTML) і вмикання / вимикання історії викликів :

private final StringPrinter stringPrinter = new StringPrinter();
...
  public String getStatistic(OutputFormatType outputFormatType, boolean history){ 
    switch(outputFormatType){ 
      case STRING: return stringPrinter.print(records,history);
... 
    } 
}
Після цього напишемо тестовий склас  методи якого нічого не робитимуть але зупинятимуть потік на якийсь час, і заанотуємо ці методи щоб виміряти їх час виконання :

public class PerformanceTestObject {
  @Stat4JPoint
  public void method2MS() throws InterruptedException { 
    Thread.sleep(2);
  }  
  
  @Stat4JPoint
  public void method25MS() throws InterruptedException {
    Thread.sleep(25);
  } 
  
  @Stat4JPoint
  public int method155MS() throws InterruptedException { 
    Thread.sleep(155);
    return 155;
  } 
  
  @Stat4JPoint
  public void method1Sec() throws InterruptedException { 
    Thread.sleep(1000);
  }
}



і відповідно сам тест :

public class Point4JAspTest { 
  @Test
  public void testPerformanceMeassuringStringOutput() throws InterruptedException {     PerformanceTestObject performanceTestObject = new PerformanceTestObject();     
    performanceTestObject.method2MS();
    performanceTestObject.method25MS();
    performanceTestObject.method25MS();
    performanceTestObject.method25MS(); 
    performanceTestObject.method25MS();
    performanceTestObject.method1Sec();
    performanceTestObject.method155MS(); 
    System.out.println(Stat4J.instance().
      getStatistic(Stat4J.OutputFormatType.STRING,true));
  }
}


Запускаємо та дивимось результат :

org.ar.sta4j.PerformanceTestObject.method25MS: Call times:   4, Max: 30011694(ns) |   30(ms), Min: 27759291(ns) |     27(ms), Avr: 28949879(ns) |       28(ms);
method25MS: Execution date: 03.03.76 14:58:04.321, Execution time: 27759291 (ns)|  27(ms);
method25MS: Execution date: 03.03.76 07:00:38.850, Execution time: 28609810 (ns)|  28(ms);
method25MS: Execution date: 03.02.76 14:27:54.968, Execution time: 29418723 (ns)|  29(ms);
method25MS: Execution date: 03.02.76 22:38:51.322, Execution time: 30011694 (ns)|  30(ms);
org.ar.sta4j.PerformanceTestObject.method2MS: Call times:   1, Max: 3253797(ns) |    3(ms), Min: 3253797(ns) |      3(ms), Avr:  3253797(ns) |        3(ms);
org.ar.sta4j.PerformanceTestObject.method1Sec: Call times:   1, Max: 1002833335(ns) | 1002(ms), Min: 1002833335(ns) |   1002(ms), Avr: 1002833335(ns) |     1002(ms);
org.ar.sta4j.PerformanceTestObject.method155MS: Call times:   1, Max: 162245276(ns) |  162(ms), Min: 162245276(ns) |    162(ms), Avr: 162245276(ns) |      162(ms);


Ось і все. Прінтери для JSON та HTML формату не став тут викладати заради економії часу проте все чого тут не вистачає ви зможене знайти тут.


Очікую ваші відгуки та пропозиції щодо даної утиліти.

П.С. Надіюсь було цікаво.



вівторок, 4 серпня 2015 р.

#JDayLviv 2015 update


19-го вересня у Львові втретє відбудеться щорічна конференція присвячена мові програмування java  та супутнім технологіям

Щороку ми намагаємось запросити нових, цікавих експертів для вибагливих українських слухачів.
Отож цього року вперше в Україні


Marco Cecconi 
 StackExchange  веб девелопер. Марко родом з Мілана, якийсь час мандрував навколо світу. Навчався в Сінгапурі, працював у Франції, Португалії і врешті осів у Великобританії.











Paris Apostolopoulos
 родом з Греції, живе та працє в Люксембурзі. Засновник Java Hellenic User Group, Java та JBoss чемпіон. Захоплюється дзюдо. Веде блог та зрідка записує подкасти











Serkan ÖZAL
 девелопер у компанії Hazelcast, Туреччина. Контрибютор у Oracle Open-Source. У вільний час працює над особистим опен сорс продуктом JIllegal











Rudy De Busscher
 працює у  contribute4j, Бельгія. JavaEE експерт, член експертної групи JSR 375 Java EE Permission











 
Grzegorz Piwowarek
 девелопер в компанії TouK. Музикант та професійний йойо гравець. Розробляє розподілені ситеми. Фанат  Docker'а









А також
 Andriy Yakovenko
 Chief Scientist у VideoGorillas,  українська зірка програмування на Java. Автор унікальних алгоритмів по обробці відео.










та інші...

Цього року конференція пройде у конференц залах стадіону Арена Львів



Більше інформації про конференцію, цих та інших спікерів ви можете знайти на нашій сторінці  www.jday.com.ua


Квитки можна придбати за посиланням

#JUGLviv: Tuning the JVM and tools for it, meetup summary


Big thank you to everyone who joined us for the meetup!

This time we had a special guest, from Poland - Tomek Borek, 
with a great presentation on how to tune the JVM.
Tomek also covered a few OS tools, which may help to identify performance bottlenecks.

Slides are available here.

Please rate the talk, at Tomek's blog, here.

Also, special thanks to Sigma Software, for supporting the event!

Pictures:






Also, congratulations to our winners:

Bohdan Hliva - won a ticket to GeeCon Microservices
Orest Gavur - won a ticket to GeeCon Prague



понеділок, 27 липня 2015 р.

#JUGLviv: Tuning the JVM and tools for it



28-го липня, о 18:30, відбудеться зустріч JUG Lviv 
за адресою вул. Олени Степанівни 49,

Вхід - традиційно вільний,
реєстрація обов'язкова!

Speaker:
Coder from Poland*. Hoping to learn something new every day.
Thankful to everybody who helps me while I hop along. Proud of: http://sckrk.com and http://geecon.org.
Co-leads: SCKRK, Polish JUG, Lambda Lounge Kraków.

I’m a flawed human, and unafraid to say so.
Into: people, software, music, role-playing, honesty, learning.
Dislikes: doing unnecessary things, doing “because I can”.


* I got it from me parents. They coded and so do I.

Talk:
Tuning the JVM and tools for it. 
Abstract: When performance hits bottom, everybody starts running around. Agitated questions are thrown at developers, who suddenly are required to have worked with performance in mind for last half a year (and still keep all those deadlines).

Yeah, people are irrational. So what can a dev do?

I'll show you just that. I'll tell you what it means to run a JVM process in GNU/Linux, what tools you have and how they can be used. We'll cover interesting flags, commands and utils you have on just a GNU/Linux box with Hotspot installed. How they can be used to just learn about what eats your memory, deal away with OOM errors and find out where you stall and why. If time permits, we'll go into tools that require installation. :-)

Подарунки:
Розіграш квитків на 




середа, 22 липня 2015 р.

#Hibernate:Comparing dates


Lets assume we have bean MyBean with field createTime

class MyBean{
public long id;

public Date createTime;

}


if you execute following code
MyBean bean = new MyBean(); 

Date time; 

bean.createTime = time; 

//save and get with hibernate 

save(bean); 

MyBean storedBean = getBeanById(bean.id); 

log.info("Times are equal: {}", storedBean.createTime.equals(time));

Variable 'time' was created and saved so that we can expect log will print "Times are equal true" but actually we'll see "Times are equal false" When hibernate fetch Date from DB it returns java.sql.Date
That's it
If you need such condition in code you can do like this
log.info("Times are equal: {}", storedBean.createTime.getTime()==time.getTime()); 

//returns Times are equal true


понеділок, 20 липня 2015 р.

#JUGLviv: Maintainability of Java applications, meetup summary


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

Окрема подяка Odesa JUG та компанії Sigma software, які долучились до організації події.



Фото (повний альбом доступний за посиланням):








 Також, вітаємо переможців, які отримають квитки на конференції:
Roman Malko - CodePot
Nazar Kushnir - DevDay
Ігор Саварин - JDayLviv

Слідкуйте за анонсами!


четвер, 16 липня 2015 р.

Spring: migration from 3.x to 4.x issue



Today we’ve got such exception
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
quick investigation on Spring sources led us to issue with json serialization.
We use jackson for converting  from object to json behind the scene
For Spring 3.x we configured
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.4.2</version>
</dependency>

 

But Spring 4.x requires jackson v2
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.5.4</version>
</dependency>
 

Thx to Xavier Padró and his blog for hint

Also you can read more about json serialization in Spring 4.x on Spring official blog


субота, 11 липня 2015 р.

#JUGLviv: 23rd meetup: Maintainability of Java applications.


Чергова зустріч JUG вже незабаром!
Цього разу, спільно з Odessa JUG.
16-го липня, о 18:30, за адресою вул. Олени Степанівни 49,

Вхід - традиційно вільний,
реєстрація обов'язкова!

Talk:
Maintainability of the Java applications. 
How to create Java application to reduce cost of long-term support and development.

Speaker:
Антон Гриценко - Senior Software Developer в Sigma Software, Одеса.
Антон приїде до нас з Одеси, щоб поділитись досвідом.

Подарунки:
Компанія Sigma підтримує JUG Lviv, і обіцяє подарунки учасникам.

Також, активні учасники матимуть шанс виграти квитки на 3 (!) конференції:







вівторок, 7 липня 2015 р.

OpenSource проект у Львові шукає контрибуторів


На днях до мене звернувся колега з проханням допомогти розповсюдити інформацію про opensource проект над яким він працює в межах аспірантської роботи в ЛНУ. Дуже потрібна допомога ком'юніті оскільки вести самому такий великий проект доволі важко. Отож всі кому цікаво взяти в корисному соціально спрямованому opensource проекті звертайтесь jug.lviv@gmail.com
Прохання стосується не тільки "самотніх рейнджерів", але й компаній які можливо мають незайнятих людей та готові помогти у розвитку корисного проекту.

Кілька слів власне по проект
В вересні 2014 року почалась розробка опенсорс проекту для управління процесами в університетах України.Мета розробки даного проекту  - інформатизація процесів всередині університету, а також розробка продукту, який би інтегрувався з ЄДЕБО (Єдина Державна Електронна База Даних з питань Освіти). Також такий продукт необхідний в силу застарілих систем, які використовує університет. Проект великий, але плюс полягає в тому, що розробивши таку систему - її можна з легкістю впроваджувати в інших навчальних закладах.

Сам проект концептуально поділений на декілька модулів, зокрема бекенд і фронтенд, також існує модуль для інтеграції з ЄДЕБО.На даному етапі йде розробка фронтенд частини для Вступної Кампанії, помагає нам в цьому компанія SoftServe. Бекенд розробляється силами Івана Урсула та бажаючими помогти студентами.

З цікавого:
 • Маємо свій Jenkins i Continuous Deployment.
 • Маємо JIRA
 • Маємо ліцензію від Intelij для всіх розробників цього продукту :)
 • Юзаємо Git, маємо свій Гітхаб https://github.com/ifnul
 • ~2300 юніт тестів
 • Код ковередж > 95 %
 • ~1300 інтеграційних тестів
 • ~950 комітів на бекенді
 • Наразі база даних налічує ~130 таблиць.

Технології(бекенд):
 • Java 8, Scala
 • Apache Maven 3, Gradle
 • Spring Core, Spring MVC, Spring Boot
 • jUnit, Mockito
 • Checkstyle, Findbugs, Cobertura
 • JPA, Hibernate 4.3
 • PostgreSQL 9.1
 • Liquibase Migrations
 • Gatling

Технології фронтенд:
 • vanilla.js :)
 • Angular.js
 • Grunt, Bower

Тому розшукуються розробники, колишні студенти або просто небайдужі люди, які б допомогли нам разом розробляти цей проект.Цікаві любі пропозиції: від самостійних ініціатив окремих розробників до компаній, які можуть надати людей з бенчу.

вівторок, 23 червня 2015 р.

#Eclipse #hotkeys I use everyday




Ctrl+Shift+LList of all available hotkeys

Ctrl+Shift+ROpen Resource (file, folder or project)
Ctrl+LJump to Line Number. To hide/show line numbers, press ctrl+F10 and select 'Show Line Numbers'
Next 3 combinations are pretty obvious but I put it here 'cause in IntelliJIdea they mean something different (hate Idea for that)
Ctrl+ZUndo last action
Ctrl+YRedo last (undone) action
Ctrl+DDelete Line
Ctrl+HSearch Workspace (Java Search, Task Search, and File Search)
Ctrl+Shift+FAutoformat all code in Editor using code formatter
Ctrl+OShow code outline / structure
F3Open Declaration: Jump to Declaration of selected class, method, or parameter
Ctrl+TShow / open Quick Type Hierarchy for selected item
Ctrl+Shift+TOpen Type in Hierarchy
Ctrl+Alt+HOpen Call Hierarchy
Alt+Shift+TRefactoring menu
Alt+Shift+SSource management menu (generate get/set/equals/etc.)
Alt+Shift+RRename selected element and all references
Ctrl+OShow code outline / structure