четверг, 14 февраля 2013 г.

Vaadin, доступ к недрам JavaScript. (перевод)

Заинтересовала тема JavaScript в Vaadin, а тут как раз Michael Vogt из команды Vaadin опубликовал небольшую статью в блоге Vaadin.
Представляю вам перевод статьи на русский, прошу поправить в комментариях, если где-то что-то не так переведено, так как опыта с переводами подобных статей пока мало.






Одним из наибольших плюсов Vaadin в том, что он определяет front-end часть на очень высоком уровне и заботиться о мелких деталях за вас. Но иногда требования к вашему приложению такие, что кое-что не может быть реализовано стандартными методами. Именно тогда вы может создать свои собственные виджеты.

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

JSNI JavaScript Native Interface спасет ваш день. Он позволит вам сделать все вышеперечисленное. Замечательно, не так ли?

Есть и загвоздки, конечно. Пожалуйста, обратите внимание, используйте это осторожно. При использовании этих методов вся ответственность лежит на вас. Придется заботиться, например, о различиях браузеров, о том, нет ли утечек памяти, о безопасности.
Для Java, тело функции JavaScript абсолютно непрозрачно, как и любой объект JavaScript.
Это значит, что для отладки JavaScript, придется использовать JavaScript отладчик, так как встроеный в Eclipse дебагер его не видит. В остальном JSNI работает очень прозрачно.

Давайте посмотрим как это работает:
- Добавьте ключевое слово native в описание функции.
- Тело функции оберните специальной комбинацией /*-{}-*/;
- Напишите ваш JavaScript между фигурными скобками.

Вот и все  JSNI функция выглядит например вот так:
private native void jsniFunction()
/*-{
           
}-*/;

или

public static native String jsniFunction( int value)
/*-{
    return "Parameter value: " + value;
}-*/;

или любой другой код.

Вас может удивить знак комментариев в начале строки, но ничего страшного, так надо, Eclipse все правильно отформатирует, проще говоря, поймет.

Теперь давайте рассмотрим решение тех 4-х ситуаций, которые были описаны выше, в начале:

- Вам необходим прямой доступ к JavaScript из клиентской части приложения.

Во-первых загрузите ваш JavaScript на главной странице. Это очень легко сделать с помощью аннотации @JavaScript({ "external.js" }) в вашем классе интерфейса. Подробное описание этого процесса здесь.
external.js может содержать что-то вроде этого:

function callme(String name) {
    alert("Hello " + name);
}

тогда вы можете получить к этому доступ так:

private native void jsniFunction() /*-{
           $wnd.callme("Vaadin");
}-*/;


- Вам необходим доступ к клиентской части из JavaScript.

Сначала нужно создать функцию, которая вызовет свой код на стороне клиента. Это требует специального синтаксиса, который выглядит так:

[instance-expr.]@class-name::method-name(param-signature)(arguments)

где:
instance-expr.: должен присутствовать при вызове экземпляра метода и отсутствовать при вызове статического метода.

class-name: это полное имя класса или его подкласса в котором объявлен метод.

param-signature : это внутренний Java метод, подписанный как определнный в JNI Type Signatures, но без указания возвращаемого методом типа, так как перегрузка метода не требуется.

arguments: список аргументов для передачи вызываемому методу.


Например:
Чтобы получить доступ к JavaScript функции, зарегистрируйте ее во время запуска вашего приложения, например как глобальный объект Window у JavaScript:

private native void registerJsniFunction() /*-{
           $wnd.jsnifunction = this.@com.example.Example::callIntoJava;
}-*/;

Примечание: код на стороне клиента запущен внутри фрейма, вы можете использовать  $wnd и $doc для обращения к нужным Window или Document элементам.

- Вам необходим доступ к некоторым функциям или полям которые объявлены как приватные в виджетах по-умолчанию.

Это может быть одна из самых недооцененных функций использования JNDI. Так как весь front-end код компилируется в JavaScript и все функции и переменные доступны глобально. Это означает, что когда ограничения доступа к Java не позволяет вам получить доступ к функции или полю, то можно легко получить его через JSNI.
Вот пример из DataGrid где мне нужен был доступ к ScrollPanel внутри него.

private native ScrollPanel getTableDataScroller()
    /*-{
       return this.@com.google.gwt.user.cellview.client.CustomDataGrid::tableDataScroller;
    }-*/;

Теперь вы можете получить доступ к нему, если он будет доступен в Java.

public HandlerRegistration addScrollHandler(ScrollHandler handler) {
           return getTableDataScroller().addScrollHandler(handler);
    }


- Код на стороне клиента компилируется слишком медленно.

Даже тогда, когда компилятор с большим усердием создал лучший на его взгляд JavaScript, ваш профайлер может сказать обратное. В этом случае вы можете представить собственную реализацию. Но, пожалуйста, в этом случае, не забывайте постить в баг-трекинг GWT.
Остались некоторые особенности, которые я не включил в примеры, для того, чтобы не усложнять их. Пожалуйста, посмотрите их в документации GWT.
В дополнение к этому посмотрите JavaScript Overlay Types, которые предоставят вам легкий доступ к JavaScript объектам, таким как JSON, например.

Я надеюсь, что мог бы показать вам больше, для понимания того, насколько мощным является JSNI. Но как говорится, с большой силой приходит и большая ответственность. Будьте, пожалуйста, осторожней ;-)

Вот и все сообщение о работе с GWT на низком-уровне. В следующий раз я расскажу как вы можете добавить WAI-ARIA  поддержку вашим виджетам.

среда, 13 февраля 2013 г.

RuntimeException и Exception, Java

Побывал на днях на собеседовании, и задали мне вопрос, чем же отличается RuntimeException от Exception?
Вопрос, элементарный, но я на нем зашился, хотя и знал и использовал и то и другое. Вопрос видимо был задан для того, чтобы понять, имеются ли общие представления о классе Exception как таковом.

Если заглянуть в Javadoc, что я при необходимости делаю, то видно, откуда растут ноги.
Если перейти выше по иерархии классов, к классу Exception, то становится понятно, что Exception для RuntimeException как и для других подобных классов является суперклассом.
Я не ответил и мне "сообщили", что RuntimeException используется для определения деления на ноль...  и всё, ни намека на что-то другое.
Но что это? В Javadoc указаны его субклассы, которые....

Вот список и описание подклассов RuntimeException:


ArithmeticalException extends RuntimeException
  Возникла исключительная ситуация, связанная с ошибкой при выполнении арифметического вычисления (например, с попыткой целочисленного деления на нуль).


IndexOutOfBoundsException extends RuntimeException
  Задано значение индекса массива или содержимого строки типа String, не принадлежащее допустимому диапазону.


ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException
  Задано значение индекса массива, не принадлежащее допустимому диапазону. Имеется дополнительный конструктор, принимающий в качестве параметра ошибочное значение индекса и включающий его в текст описательного сообщения.


StringIndexOutOfBoundsException extends IndexOutOfBoundsException
  Задано значение индекса содержимого строки типа String, не принадлежащее допустимому диапазону. Имеется дополнительный конструктор, принимающий в качестве параметра ошибочное значение индекса и включающий его в текст описательного сообщения.


ArrayStoreException extends RuntimeException
  Предпринята попытка сохранения в массиве объекта недопустимого типа.

ClassCastException extends RuntimeException
  Выполнена неверная операция преобразования типов.

ConcurrentModificationException extends RuntimeException
  Осуществлена попытка изменения объекта конкурирующим потоком вычислений (thread) с нарушением контракта класса (тип определен в пакете jav.util).

EmptyStackException extends RuntimeException
  Выполнена операция выталкивания (pop) значения из пустого стека. тип обладает только конструктором без параметров, поскольку причина ситуации очевидна без дополнительных разъяснений (тип определен в пакете java.util).

IllegalArgumentException extends RuntimeException
  Методу передано неверное значение аргумента (например, оприцательное, когда метод предполагает задание положительных значений).


IllegalThreadStateException extends IllegalArgumentException
  Предпринята попытка выполнения операции в то время, когда объект потока вычислений не находится в соответствующем состоянии (например, вызван метод start для потока, который уже приступил к работе).


NumberFormatException extends IllegalArgumentException
  Строка, которая, как предполагалось должна содержать представление числа, не отвечает этому требованию. Исключение выбрасывается такими методами, как, например, Integer.parseInt.



IllegalMonitorStateException extends RuntimeException
  Выполнено обращение к методу wait, notifyAll или notify объекта, когда текущий поток вычислений не обладает блокировкой (lock) этого объекта.

IllegalStateException extends RuntimeException
  Предпринята попытка выполнения операции в то время, когда объект не находится в соответствующем состоянии (например при регистрации или удалении ловушки события закрытия исполняющей системы (shutdown hook) после начала процедуры закрытия).

MissingResourceException extends RuntimeException
  Не найден требуемый ресурс или пакет ресурсов (resource bundle). Единственный конструктор типа предусматривает задание трех аргументов: строки описательного сообщения, наименования клсса ресурсов и объекта ключа, отвечающего отсутствующему ресурсу. Для получения строк наименования класса и ключа применяются методы detClassName и getKey соответственно (тип определен в пакете java.util).

NegativeArraySizeException extends RuntimeException
  Предпринята попытка создания массива с размером, значение которого задано в виду отрицательного числа.

NoSuchElementException extends RuntimeException
  Операция поиска элемента в объекте одного из контейнерных классов завершилась неудачей (тип определен в пакете java.util).

NullPointerException extends RuntimeException
  Для доступа к полю или методу была применена ссылка, равная null. Исключение выбрасывается и той ситуации, когда метод, не допускающий передачи аргумента null, был вызван с заданием значения null. В последнем случае может быть сгенериговано и исключение типа IllegalArgumentException.

SecurityException extends RuntimeException
  Предпринята попытка выполнения операции, запрещенной системой обеспечения безопасности в соответствии с действующей политикой безопасности.

UndeclaredThrowableException extends RuntimeException
  Выбрасывается при обращении к методу целевого объекта посредством объекта рефлективного класса Proxy, если метод invoke объекта InvocationHandler генерирует объявляемое исключение, которое не допускает присваивания ниодному из типов исключений, упомянутых в предложении throws метода целевого объекта. Рассматриваемое исключение содержит ссылку на исключение, генерируемое методом invoke, которое может быть получено с помощью метода getUndeclaredThrowable. Класс исключений UndeclaredThrowableException поддерживает два конструктора: оба принимают в качестве параметров ссылку на объект Throwable, а один из них, помимо того, строку описания (тип определен в пакете java.lang.reflect).

UnsupportedOperationException extends RuntimeException
  Предпринята попытка выполения операции над объектом, который ее не поддерживает (например, модификация объекта, обозначенного признаком "только для чтения"). используется также классами коллекций из состава пакета java.util как реакция на вызов методов производного класса, реализация которых не обязательна.

Переводить с офф сайта не стал, переводы взял отсюда. Теперь я, да и вы, в курсе, что прежде чем идти на собеседование, неплохо было бы повторить основы Java. Что именно? Примерные вопросы на собеседовании есть по всему интернету, ищите.
Не смотря на то, что вы сможете выполнить тестовое задание, знание теоретических основ могут оказать существенное влияние на решение о вашем трудоустройстве :) Ведь предполагается, что не зная основ, человек не способен выполнять рабочие обязанности. Здесь есть доля правды, причем бОльшая доля.
По возможности попросите работодателя дать примерный план собеседования и какие специалисты будут участвовать в нем. Потому как, не уточнив это и ожидая собеседования с одним человеком, вы можете столкнутся с 4-5-ю специалистами.
К этому надо быть готовым, удачи! :)

вторник, 12 февраля 2013 г.

Группа Vaadin в Вконтакте.

Создал на днях группу в ВК о Vaadin, постараюсь почаще в нее кидать ссылки на различные ресурсы о сабже. В ней же можно задать вопросы, создать обсуждение, ну и просто высказаться по теме.
Присоединяйтесь, ЗДЕСЬ вам рады!!! )))

понедельник, 11 февраля 2013 г.

Отличия Vaadin 7 от Vaadin 6.

Решил сюда складывать, те или иные вещи, которые изменились с приходом 7-й версии Vaadin.
Практически всё что будет здесь, так или иначе было взято из гугла и/или форума Vaadin.


1. Наследование классов от UI, а не от Application.
com.vaadin.Application  -> com.vaadin.ui.UI
В 6-й было:
public class Login extends Application {.....}
В 7-й стало:
public class Login extends UI {.....}

2. Заголовок страницы. То есть то, что отображается в названии, допустим, вкладки Chrome.
В 6-й было:
Window mainWindow = new Window("Авторизация");
setMainWindow(mainWindow);
В 7-й стало:
Page.getCurrent().setTitle("Авторизация");


3. Компонент Window теперь не может использоваться в качестве главного окна
В 7-й версии не обнаружил у Window и у Panel метода addComponent(), говорят и у некоторых других его нет. Чтобы добавить в окно компоненты, нужно сначала их положить в лайаут, например в VerticalLayout, а затем через    .setContent(layout) включить этот слой с компонентами в окно.
например:
   Window mainLoginWindow = new Window();
   VerticalLayout mainVL = new VerticalLayout();
mainVL.addComponent(loginNS);
mainVL.addComponent(passwordField);
mainVL.addComponent(enterButton);
   mainLoginWindow.setContent(mainVL);


4. Компонент Button, изменился листенер.
Старый листенер стал деприкейтид, вместо него, теперь нужно указывать более конкретный листенер  :)
В 6-й было:
enterButton.addListener(new Button.ClickListener() {
            public void buttonClick(ClickEvent event) {
 ............................
            }
        });
В 7-й стало:
enterButton.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
 ............................
            }
        });
Наверняка у других компонентов произошли подобные изменения слушателей, поэтому больше про изменения в них писать не стану.

пятница, 8 февраля 2013 г.

Vaadin 7.0.0, Netbeans. Как быстро создать проект.

Заранее прошу прощения за слишком подробное описание, но так можно избежать многих лишних вопросов от тех, кто никогда не работал в Netbeans и/или c Vaadin.

С выходом стабильной 7-й версии веб-фреймворка Vaadin, захотелось создать тестовый проект, а так же позже перетащить основной с 6-ки на 7-ку.
Здесь я не стану описывать как создать проект с помощью, скажем так, архитектурного файла maven, потому как, практически им не пользуюсь за ненадобностью (когда понадобится - разберусь подробно :) ), все это есть на сайте Vaadin'а.
Создаем простой проект с локальными библиотеками vaadin. Такой, чтобы можно было запустить и отобразить надпись "Hello Vaadin!" с помощью компонентов фреймворка.

И так поехали, что нам нужно (будем брать последние на текущий момент версии):
- Netbeans 7.2
- Пакет с jar-файлами, качаем отсюда.

Погнали:
Создаем новый проект в Netbeans:
- File - New Project - Java Web - Web Application.
- Пишем имя проекта в поле Project Name, назовем его "Vaadin7". При желании, выберем расположение папки проекта.
- Next
- Выбираем в выпадающем меню наш сервер приложений. У меня это Apache Tomcat. Выбор сервера приложений это отдельная тема. Выбираем версию Java EE, как правило работаем с 6-й.
- Поле Context Path я очистил, это путь, который будет отображаться в браузере, сразу после локального адреса, например наш Context Path = /gtm это значит, что в браузере при запуске будет написано что-то вроде этого http://localhost:8080/gtm. Я же хочу, чтобы проект грузился просто при заходе по http://localhost:8080/. Этот параметр можно настроить позже в файле context.xml или в свойствах проекта.
- Next
- На следующей странице нам предлагают выбрать фреймворки, пропускаем это окно и жмем Finish.

Проект создан, но главная страница у него это *.jsp, нам это не нужно, мы хотим Vaadin :)
По умолчанию структура такая:










- Удаляем файл index.jsp

Создаем файл web.xml в каталоге WEB-INF.
- Жмем правой кнопкой по этой папке
- New - Other - XML - XML Document.
- Называем его web, но сам файлик, как вы увидите в поле ниже, будет в расширением xml.
- Выбираем в следующем окне тип нашего XML - Well-formed Document (по умолчанию) и жмем Finish.

Удаляем все что в нем понаписано и вставляем следующий текст:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
    id="WebApp_ID" version="2.4"
                   xmlns="http://java.sun.com/xml/ns/j2ee"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
     http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
    <servlet>
        <servlet-name>Login</servlet-name>
       <servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
        <init-param>
            <description>Vaadin application class to start</description>
            <param-name>UI</param-name>
            <param-value>nix.common.LoginForm</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Login</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
 
</web-app>


- Сохраняем.


Теперь создадим в папке Source Packages новые папки/пакеты.
- Правой кнопкой по папке
- New - Other - Other - Folder. Либо New - Java - Java Package.
(По факту не важно что вы добавите папку или пакет, для Java оно не важно, физически пакеты это папки.)
- Назовем первую папку nix.
- Таким же макаром в папке nix, создадим папку common.
- В папке common создадим java-файл с именем LoginForm.java. Создается точно так же как и папки/пакеты, но выбираем New - Other - Java - Java Class. (Все добавленные ранее типы объектов остаются в списке быстрого добавления, думаю вы уже заметили).
Пока файл трогать не будем, давайте добавим в проект скачанные ранее библиотеки Vaadin 7.

Распакуем скачанный архив, вот его структура:























Нам понадобятся 8 файлов, 7 из корневой папки:
- vaadin-client-7.0.0.jar
- vaadin-client-compiled-7.0.0.jar
- vaadin-client-compiler-7.0.0.jar
- vaadin-server-7.0.0.jar
- vaadin-shared-7.0.0.jar
- vaadin-theme-compiler-7.0.0.jar
- vaadin-themes-7.0.0.jar

и один из папки libs:
- jsoup-1.6.3.jar

Соберем их все в одну папку и сохраним куда-нибудь отдельно, туда, где мы будем хранить библиотеки.
Щелкаем правой кнопкой по папке Libraries и может:
- Add JAR/Folder  (сразу добавляем все наши файлы к проекту, для примера я так и сделал).
- Add Library (Если вы через меню Netbeans'a, Tools - Ant Libraries, добавили и как-нибудь назвали свою библиотеку).
В итоге получили такую вот структуру проекта:






















Вернемся к нашему java классу LoginForm.java, дополним его кодом, получим следующее содержание:


package nix.common;

import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

public class LoginForm extends UI {

    @Override
    protected void init(VaadinRequest request) {
        VerticalLayout view = new VerticalLayout();
        view.addComponent(new Label("Hello Vaadin!"));
        setContent(view);
    }
}

Вот и все.
Теперь, если вы все сделали верно, то при запуске откроется ваш браузер по-умолчанию и в нем будет надпись "Hello Vaadin!" на сером фоне.



Основные, отличия 7-го Vaadin'а от 6-го в плане настроек это то, что теперь вместо пакета
com.vaadin.Application
используется
com.vaadin.ui.UI
Что и было использовано в java-классе.

Кроме того, пришлось изменить и web.xml.
Раньше было: <servlet-class>com.vaadin.terminal.gwt.server.ApplicationServlet</servlet-class>
Теперь: <servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
и
Раньше было:
                       <init-param>
            <param-name>application</param-name>
            <param-value>nix.common.LoginForm</param-value>
                      </init-param>
Теперь:
                      <init-param>

            <param-name>UI</param-name>
            <param-value>nix.common.LoginForm</param-value>
                     </init-param>

Кроме того, теперь не нужно качать отдельно пакеты GWT и компилить их для проекта, они уже включены в библиотеки в том виде, в котором нужно.
Есть вопросы - пишите их в комментариях, а я пока пошел добавлять другой функционал в тестовый проект :)