Java — Как правильно спроектировать общение с базой данных


Содержание

Работа с БД из Access и Java

Доброго времени суток.
На практике преподаватель задал задание, сделать программу с интерфейсом в NetBeans. Задача следующая: на форме две кнопки, поле со списком и таблица. По нажатию на 1 кнопку подключается сама БД, в список заносятся названия таблиц, по нажатия на 2 кнопку выбранная таблица отображается ниже. Всё сделал, всё достаточно просто.

Мне дали доп задание. В таблице есть поле с изображением, вместо которого пишется «код» изображения. Необходимо по 3 кнопке вывести изображение на форму. Были идеи вывести изображение используя запрос на SQL, но, в силу 5-ти дневного общения с Java и NetBeans, реализовать ни одну из идей я не смог, а это задание дано мне в качестве зачёта к практике и отчёт необходимо сдать 5 мая.

Прошу помочь. Если вопрос где то был, прост или глуп не ругайте, с Java действительно знаком буквально в течении недели.

30.04.2014, 14:48

Что лучше: работа с Access через ADO.NET или силами самого Access?
Считаю, что лучше работать с регулярно поступающим в Access объемом данных, разработав приложение.

Работа Senior Java Developer (Java-разработчик)
Задачи: • Создание высоконагруженных web приложений используя одну из ведущих enterprise платформ.

Возможна ли работа проги на Access без Access?
Не подскажете возможна ли работа проги на Access без установленного на машине Access? Т.е.

Java и MS Access
помогите с инфой по созданию БД с нуля

Проектирование архитектуры базы данных для чата

В чате должны быть созданы следующие таблицы:

  • таблица со списком чатов
  • таблица со списком участников чата
  • таблица со списком сообщений
  • таблица со статусами сообщений

Как вообще, должен работать чат. У вас есть пользователи, которые могут общаться в чате. Чат может состоять из нескольких групп (таблица chat), по сути, из отдельных чатов в каждом из которых могут общаться пользователи (таблица party). Сообщения каждого чата добавляются (таблица messages) от участников чата, которым можно показывать уведомления о добавлении новых сообщений и проверять, какие сообщения пользователь уже прочитал (таблица message_status).

Полагаю, что у вас уже есть таблица users, в которой хранятся пользователи.

Таблица со списком групп чата

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

Название таблицы — chat

Наименование Описание
chat_id порядковый id чата
name заголовок чата, его название
user_id id пользователя создавшего чат

Таблица со списком участников чата

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

Название таблицы — party

Наименование Описание
chat_id id группы чата
user_id id пользователя, который учавствует в переписке чата

Таблица со списком сообщений

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

Название таблицы — messages

Наименование Описание
message_id порядковый id сообщения
chat_id id чата
user_id id пользователя, который добавил сообщение
contect содержимое сообщения
date_create дата добавления сообщения

Таблица со статусами сообщений

В таблице со статусами сообщений будут храниться статусы уведомлений, определяющих просмотрел ли определенный пользователь новое добавленное сообщение. Таблица выглядит следующим образом:

Название таблицы — message_status

Наименование Описание
message_id id сообщения
user_id id пользователя
is_read прочитано ли сообщение

Доводилось ли вам создавать чат для своего сайта? Если да, то какая архитектура в нем используется?

Jsf работа с базой данных

Веб-программирование на Java /

Основы веб-программирования на Java

12 апр 2010 20:08

Доброго времени суток.
Я новичок в сфере JavaEE, начинаю изучать jsf 2.0. Возник вопрос работы с базой. Я так понимаю самое правильное это работать через Hibermate ?

Как устроено общение jsf с базой?
Я так понимаю создается класс с подключением в настройках выставляется время жизни подключения сессия или иной вариант, и потом конектимся к бд средствами самодельного класса?

Можете порекомендовать инфу по данному вопросу (jsf + database)

Thinking In Java Enterprise (русский перевод) → Сетевое программирование с Сокетами и Каналами

Одна из сильных сторон Java заключается в безпроблеммной сетевой работе. Дизайнеры сетевой библиотеки Java сделали ее достаточно простой для чтения и записи файлов, за исключением случая, когда «файл» существует на удаленной машине и удаленная машина может решать что ей делать с информацией, которую вы запрашиваете или посылаете. Насколько это возможно, низлежащие детали сетевого взаимодействия были абстрагированы и о них заботится ядро JVM и локальный пакет установки Java. Программная модель, которую вы используете для такого файла, фактически, это обертка сетевого соединения («сокет») с объектом потока, так что в конечном счете вы используете те же вызовы методов, которые вы используете для других потоков. Кроме того, встроенная многопоточность Java исключительно удобна, когда вы имеете дело с такой сетевой возможностью, как обработка множества соединений одновременно.

Этот раздел является вводным в сетевое взаимедействие Java с использованием легких в понимании примеров.

Идентификация машины

Конечно, для того, чтобы передать данные с одной машины на другую необходимо убедиться, что вы подсоединились к определенной машине в сети. Ранние варианты сетей были удовлетворены предоставлением уникальных имен машинам внутри локальной сети. Однако, Java работает в пределах Internet, что требует способа для уникальной идентификации машины из любой точки всего мира. Это выполняется с помощью IP (Internet Protocol) адреса, который может существовать в двух формах:

  1. Привычная форма DNS (Domain Name System). Мое доменное имя — bruceeckel.com, и если у меня есть компьютер, называемый Opus в моем домене, его доменное имя должно быть Opus.bruceeckel.com. Это в точности имя такого рода, которое вы используете при отсылке электронной почты людям, и часто он встроен в адрес World Wide Web.
  2. Альтернативный вариант: вы можете использовать форму из четырех чисел, разделенных точками, например 123.255.28.120.

В обоих случаях IP адрес представляется как 32-х битное число [1] (так как каждое из четырех чисел не может превышать 255), и вы можете получить специальный Java объект для представления этого числа из любой из перечисленных выше форм, используя статический метод InetAddress.getByName( ), который определен в java.net. Результатом будет объект типа InetAddress, который вы можете использовать для создания «сокета», как вы это увидите далее.

В качестве простейшего примера использования InetAddress.getByName() рассмотрим, что произойдет при использовании коммутируемого доступа (dial-up Internet service provider (ISP)). При каждом дозвоне вам назначается временный IP адрес. Но пока вы соединены, ваш IP адрес имеет такую же силу, как и другие IP адреса в Internet. Если кто-либо соединится с вашей машиной использую ваш IP адрес, то он может соединится с Web сервером или FTP сервером, который запущен на вашей машине. Конечно, ему необходимо знать ваш IP адрес, а так как при каждом дозвоне вам назначается новый адрес, то как вы можете определеть какой у вас адрес?

Приведенная ниже программа использует InetAddress.getByName( ) для воспроизведения вашего IP адреса. Для ее использования вы должны знать имя вашего компьютера. Под управлением Windows 95/98 перейдите в «Settings», «Control Panel», «Network» и выберите закладку «Identification». Содержимое в поле «Computer name» является той строкой, которую необходимо поместить в командную строку.

//: c15:WhoAmI.java
// Нахождение вашего сетевого адреса, когда
// вы соединены с Internet’ом.
// <Запускается руками>Должно быть установлено соединение с Internet
//
import java.net.*;

public class WhoAmI <
public static void main ( String [] args ) throws Exception <
if ( args.length != 1 ) <
System.err.println ( «Usage: WhoAmI MachineName» ) ;
System.exit ( 1 ) ;
>
InetAddress a = InetAddress.getByName ( args [ 0 ]) ;
System.out.println ( a ) ;
>
> // /:

В моем случае, машина называется «peppy». Так что, когда я соединюсь с моим провайдером и запущу программу:

Я получу назад сообщение такого типа (конечно же, адрес отличается при каждом новом соединении):

Если я скажу этот адрес моему другу и у меня будет запущен Web Сервер на моем компьютере, он сможет соединится с сервером, перейдя по ссылке http://199.190.87.75 (только до тех пор, пока я остаюсь соединенным во время одной сессии). Иногда это может быть ручным способом распределения информации кому-то еще или использоваться для тестирования конфигурации Web сайта перед размещением его на «реальном» сервере.

Серверы и клиенты

Основное назначение сети состоит в том, чтобы позволить двум машинам соединиться и пообщаться друг с другом. Так как две машины могут найти друг друга, они могут провести милую, двусторонню беседу. Но как они могут найти друг друга? Это похоже на поиск потерянных в парке аттракционов: одна машина стоит в одном месте и слушает, пока другая машина скажет: «Эй, ты где?».

Машина, которая стоит в одном месте, называется сервером, а машина, которая ищет, называется клиентом. Это различие важно лишь до тех пор, пока клиент пробует соединится с сервером. Как только они соединятся, они становятся двумя сторонами коммуникационного процесса и более не имеет значения, какая машина принимала роль сервера, а какая принимала роль клиента.

Таким образом, работа сервера состоит в прослушивании соединения, она выполняется с помощью специального объекта, который вы создаете. Работа клиента состоит в попытке создать соединение с сервером, и это выполняется с помощью специального клиентского объекта, который вы создаете. Как только соединение установлено, вы увидите, что и клиентская, и серверная сторона соединения магическим образом превращается потоковый объект ввода/вывода, таким образом вы можете трактовать соединение, как будто вы читаете и пишете файл. Таким образом, после установки соединения, вы просто используете хорошо знакомые команды ввода/вывода из главы 11. Это одна из прекраснейших особенностей работы по сети в Java.

Тестирование программ без сети

По многим причинам, вы можете не иметь клиентской машины, серверной машины и сети, доступных для тестирования ваших программ. Вы можете выполнять упражнения в обстановке классной комнаты, или, возможно, вы пишите программы, которые еще не достаточно стабильны и не могут быть выложены в сеть. Создатели Internet Protocol учли эту возможность и создали специальный адрес, называемый localhost, IP адрес «локальной заглушки (local loopback)» для тестирования без использования сети. Общий способ для получения такого адреса в Java такой:

Если вы передадите в getByName( ) значение null, метод по умолчанию будет использовать localhost. InetAddress является тем, что вы используете для указания определенной машины, и вы должны произвести его прежде, чем вы можете двинуться далее. Вы не можете манипулировать содержимым InetAddress (но вы можете напечатать его, как это будет показано в следующем примере). Единственный способ, которым вы можете создать InetArddress, это через один из перегруженных статических методов класса getByName( ) (который является тем, что вы уже использовали), getAllByName(), или getLocalHost( ).

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

(предполагается, что «localhost» сконфигурирован в таблице «hosts» на вашей машине), или используя цифровую четырехзначную форму для имени, представляющем заглушку:

Все три формы произовдят одинаковый результат.


Порт: уникальное место внутри машины

IP адреса не достаточно для уникальной идентификации сервера, так как многие сервера могут существовать на одной машине. Каждая IP машина также содержит порты, и когда вы устанавливаете клиента или сервер, вы должны выбрать порт, через который и клиент, и сервер согласны соединиться.

Порт — это не физическое расположение в машине, а программная абстракция (в основном для целей учета). Клиентская программа знает, как соединится к машине через ее IP адрес, но как она может присоединится к определенной службе (потенциально, к одной из многих на этой машине)? Таким образом номер порта стал вторым уровнем адресации. Идея состоит в том, что при запросе определенного порта вы запрашиваете службу, ассоциированную с этим номером порта. Служба времени — простейший пример службы. Обычно каждая служба ассоциируется с уникальным номером порта на определенной серверной машине. Клиент должен предварительно знать, на каком порту запущена нужная ему служба.

Системные службы зарезервировали использование портов с номерам от 1 до 1024, так что вы не можете использовать этот или любой другой порт, про который вы знаете, что он задействован. Первым выбором, например, в этой книге будет порт 8080 (в память многоуважаемого 8-битного процессора 8080 от Intel в моем первом компьютере, CP/M машине).

Сокеты

Сокет — это программная абстракция, используемая для представления «терминалов» соединения между двумя машинами. Для данного соединения есть сокет на каждой машине, и вы можете представить гипотетический «кабель», включенный в сокет. Конечно, физическое оборудование и каблирование между машинами полностью неизвестно. Главное назначение абстракции состоит в том, что мы не должны знать более того, что нам необходимо.

В Java вы создаете сокет, чтобы создать соединение с другой машиной, затем вы получаете InputStream и OutputStream (или, с соответствующими конверторами, Reader и Writer) из сокета, чтобы получить возможность трактовать соединение, как объект потока ввода/вывода. Существует два класса сокетов, основанных на потоках: ServerSocket, который использует сервер для «прослушивания» входящих соединения, и Socket, который использует клиент для инициализации соединения. Как только клиент создаст сокетное соединение, ServerSocket возвратит (посредством метода accept( )) соответствующий Socket, через который может происходить коммуникация на стороне сервера. После этого вы общаетесь в соединении через Socket с Socket’ом и вы трактуете оба конца одинаково, посколько они и являются одним и тем же. На этой стадии вы используете методы getInputStream( ) и getOutputStream( ) для получения соответствующих объектов InputStream’а и outputStream’а для каждого сокета. Они должны быть обернуты внутрь буферных и форматирующих классов точно так же, как и другие объекты потоков, описанные в Главе 11.

Использование термина ServerSocket может показаться другим примером сбивающей с толку схемы именования в библиотеках Java. Вы можете подумать, что ServerSocket лучше было бы назвать «ServerConnector» или как-то подругому, без слова «Socket» внутри. Вы также можете подумать, что ServerSocket и Socket должны оба наследоваться от какого-то общего базового класса. На самом деле, два калсса имеют некоторые общие методы, но не настолько, чтобы дать им общий базовый класс. Вместо этого, работа ServerSocket’а состоит в том, чтобы ждать, пока некоторая машина не присоединится к нему, а затем он возвращает реальный Socket. Вот почему кажется, что ServerSocket назван немножко неправильно, так как его работа состоит не в том, чтобы быть реальным сокетом, а в том, чтобы создавать объект Socket’а, когда кто-то присоединяется к нему.

Однако, ServerSocket создает физический «сервер» или слушающий сокет на хост-машине. Этот сокет слушает входящие соединения, а затем возвращает «связанный» сокет (с определенными локальной и удаленной конечными точками) посредством метода accept( ). Сбивающая часть состоит в том, что оба эти сокета (слушающий и связанный) ассоциированы с одним и тем же серверным сокетом. Слушающий сокет может принять только новый запрос на соединение, а не пакет данных. Так что, не смотря на то, что ServerSocket имеет мало смыла с точки зрения программирования, в нем много смысла «физически».

Когда вы создаете ServerSocket, вы даете ему только номер порта. Вы не даете ему IP адрес, поскольку он уже есть на той машине, на которой он представлен. Однако когда вы создаете Socket, вы должны передать ему и IP адрес, и номер порта, к которому вы хотите присоединиться. (Однако Socket, который возвращается из метода ServerSocket.accept( ) уже содержит всю эту информацию.)

Простейший сервер и клиент

Этот пример покажет простейшее использование серверного и клиентского сокета. Все, что делает сервер, это ожидает соединения, затем использует сокет, полученный при соединении, для создания InputStream’а и OutputStream’а. Они конвертируются в Reader и Writer, которые оборачиваются в BufferedReader и PrintWriter. После этого все, что будет прочитано из BufferedReader’а будет переправлено в PrintWriter, пока не будет получена строка «END», означающая, что пришло время закрыть соединение.

Клиент создает соединение с сервером, затем создает OutputStream и создает некоторую обертку, как и в сервере. Строки текста посылаются через полученный PrintWriter. Клиент также создает InputStream (опять таки, с соответствующей конвертацией и оберткой), чтобы слушать, что говорит сервер (который, в данном случае, просто отсылает слова назад).

И сервер, и клиент используют одинаковый номер порта, а клиент использует адрес локальной заглушки для соединения с сервером на этой же самой машине, так что вы не можете провести тест по сети. (Для некоторых конфигураций вам может понадобиться сетевое соединения для работы программы даже, если вы не используете сетевую коммуникацию.)

//: c15:JabberServer.java
// Очень простой сервер, который просто отсылает
// назад все, что посылает клиент.
//
import java.io.*;

public class JabberServer <
// Выбираем порт вне пределов 1-1024:
public static final int PORT = 8080 ;

public static void main ( String [] args ) throws IOException <
ServerSocket s = new ServerSocket ( PORT ) ;
System.out.println ( «Started: » + s ) ;
try <
// Блокирует до тех пор, пока не возникнет соединение:
Socket socket = s.accept () ;
try <
System.out.println ( «Connection accepted: » + socket ) ;
BufferedReader in = new BufferedReader ( new InputStreamReader (
socket.getInputStream ())) ;
// Вывод автоматически выталкивается из буфера PrintWriter’ом
PrintWriter out = new PrintWriter ( new BufferedWriter (
new OutputStreamWriter ( socket.getOutputStream ())) , true ) ;
while ( true ) <
String str = in.readLine () ;
if ( str.equals ( «END» ))
break ;
System.out.println ( «Echoing: » + str ) ;
out.println ( str ) ;
>
// Всегда закрываем два сокета.
>
finally <
System.out.println ( «closing. » ) ;
socket.close () ;
>
>
finally <
s.close () ;
>
>
> // /:

Вы можете видеть, что для ServerSocket’а необходим только номер порта, а не IP адрес (так как он запускается на локальной машине!). Когда вы вызываете accept( ), метод блокирует выполнение до тех пор, пока клиент не попробует подсоединится к серверу. То есть, сервер ожидает соединения, но другой процесс может выполнятся (смотрите Главу 14). Когда соединение установлено, метод accept( ) возвращает объект Socket, представляющий это соединение.

Здесь тщательно обработана отвественность за очистку сокета. Если конструктор ServerSocket завершится неудачей, программа просто звершится (обратите внимание, что мы должны предположить, что конструктор ServerSocket не оставляет никаких открытых сокетов, если он зваершается неудачей). По этой причине main( ) выбрасывает IOException, так что в блоке try нет необходимости. Если конструктор ServerSocket завершится успешно, то все вызовы методов должны быть помещены в блок try-finally, чтобы убедиться, что блок не будет покинут ни при каких условиях и ServerSocket будет правильно закрыт.

Аналогичная логика используется для сокета, возвращаемого из метода accept( ). Если метод accept( ) завершится неудачей, то мы должны предположить, что сокет не существует и не удерживает никаких ресурсов, так что он не нуждается в очистке. Однако если он закончится успешно, то следующие выражения должны быть помещены в блок try-finally, чтобы при каких-либо ошибках все равно произошла очистка. Позаботится об этом необходимо, потому что сокеты используют важные ресурсы, не относящиеся к памяти, так что вы должны быть прилежны и очищать их (так как в Java нет деструкторов, чтобы сделать это за вас).

И ServerSocket и Socket, производимый методом accept( ), печатаются в System.out. Это означает, что автоматически вызывается их метод toString( ). Вот что он выдаст:

Короче говоря, вы увидите как это соответствует тому, что делает клиент.

Следующая часть программы выглядит, как открытие файла для чтения и записи за исключением того, что InputStream и OutputStream создаются из объекта Socket. И объект InputStream’а и OutputStream’а конвертируются в объекты Reader’а и Writer’а с помощью «классов-конвертеров» InputStreamReader и OutputStreamreader, соответственно. Вы можете также использовать классы из Java 1.0 InputStream и OutoutStream напрямую, но, с точки зрения вывода, есть явное преимущество в использовании этого подхода. Оно проявляется в PrintWriter’е, который имеет перегруженный конструктор, принимающий в качестве второго аргумента флаг типа boolean, указывающий, нужно ли автоматическое выталкивание буфера вывода в конце каждого выражения println( ) (но не print( )). Каждый раз, когда вы записываете в вывод, буфер вывода должен выталкиваться, чтобы информация проходила по сети. Выталкивание важно для этого конкретного примера, поскольку клиент и сервер ожидают строку от другой стороны, прежде, чем приступят к ее обработке. Если выталкивание буфера не произойдет, информация не будет помещена в сеть до тех пор, пока буфер не заполнится, что может привести к многочисленным проблемам в этом примере.

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

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

В бесконечном цикле while происходит чтение строк из входного BufferedReader’а и запись информации в System.out и в выходной PrintWriter. Обратите внимание, что вход и выход могут быть любыми потоками, так случилось, что они связаны с сетью.

Когда клиент посылает строку, содержащую «END», программа прекращает цикл и закрывает сокет.

//: c15:JabberClient.java
// Очень простой клиент, который просто посылает
// строки на сервер и читает строки,
// посылаемые сервером.
//
import java.net.*;

public class JabberClient <
public static void main ( String [] args ) throws IOException <
// Передаем null в getByName(), получая
// специальный IP адрес «локальной заглушки»
// для тестирования на машине без сети:
InetAddress addr = InetAddress.getByName ( null ) ;
// Альтернативно, вы можете использовать
// адрес или имя:
// InetAddress addr =
// InetAddress.getByName(«127.0.0.1»);
// InetAddress addr =
// InetAddress.getByName(«localhost»);
System.out.println ( «addr = » + addr ) ;
Socket socket = new Socket ( addr, JabberServer.PORT ) ;
// Помещаем все в блок try-finally, чтобы
// быть уверенным, что сокет закроется:
try <
System.out.println ( «socket = » + socket ) ;
BufferedReader in = new BufferedReader ( new InputStreamReader ( socket
.getInputStream ())) ;
// Вывод автоматически Output быталкивается PrintWriter’ом.
PrintWriter out = new PrintWriter ( new BufferedWriter (
new OutputStreamWriter ( socket.getOutputStream ())) , true ) ;
for ( int i = 0 ; i 10 ; i++ ) <
out.println ( «howdy » + i ) ;
String str = in.readLine () ;
System.out.println ( str ) ;
>
out.println ( «END» ) ;
>
finally <
System.out.println ( «closing. » ) ;
socket.close () ;
>
>
> // /:

В main( ) вы можете видеть все три способа получение InetAddress IP адреса локальной заглушки: с помощью null, localhost или путем явного указания зарезервированного адреса 127.0.0.1, если вы хотите соединится с машиной по сети, вы замените это IP адресом машины. Когда печатается InetAddress (с помощью автоматического вызова метода toString( )), то получается результат:

При передачи в getByName( ) значения null, он по умолчанию ищет localhos и затем производит специальныйы адрес 127.0.0.1.

Обратите внимание, что Socket создается при указании и InetAddress’а, и номера порта. Чтобы понять, что это значит, когда будете печатать один из объектов Socket помните, что Интернет соединение уникально определяется четырьмя параметрами: клиентским хостом, клиентским номером порта, серверным хостом и серверным номером порта. Когда запускается сервер, он получает назначаемый порт (8080) на localhost (127.0.0.1). Когда запускается клиент, он располагается на следующем доступном порту на своей машине, 1077 — в данном случае, который так же оказался на той же самой машине (127.0.0.1), что и сервер. Теперь, чтобы передать данные между клиентом и сервером, каждая сторона знает, куда посылать их. Поэтому, в процессе соединения с «известным» сервером клиент посылает «обратный адрес», чтобы сервер знал, куда посылать данные. Вот что вы видите среди выводимого стороной сервера:

Это означает, что сервер просто принимает соединение с адреса 127.0.0.1 и порта 1077 во время прослушивания локального порта (8080). На клиентской стороне:

Это значит, что клиент установил соединение с адресом 127.0.0.1 по порту 8080, используя локальный порт 1077.

Вы заметите, что при каждом повторном запуске клиента номер локального порта увеличивается. Он начинается с 1025 (первый после зарезервированного блока портов) и будет увеличиваться до тех пор, пока вы не перезапустите машину, в таком случае он снова начнется с 1025. (На машинах под управлением UNIX, как только будет достигнут верхний предел диапазона сокетов, номер будет возвращен снова к наименьшему доступному номеру.)

Как только объект Socket будет создан, процесс перейдет к BufferedReader и PrintWriter, как мы это уже видели в сервере (опять таки, в обоих случаях вы начинаете с Socket’а). В данном случае, клиент инициирует обмен путем посылки строки «howdy», за которой следует число. Обратите внимание, что буфер должен опять выталкиваться (что происходит автоматически из-за второго аргумента в конструкторе PrintWriter’а). Если буфер не будет выталкиваться, процесс обмена повиснет, поскольку начальное «howdy» никогда не будет послана (буфер недостаточно заполнен, чтобы отсылка произошла автоматически). Каждая строка, посылаемая назад сервером, записывается в System.out, чтобы проверить, что все работает корректно. Для завершения обмена посылается ранее оговоренный «END». Если клиент просто разорвет соединение, то сервер выбросит исключение.

Вы можете видеть, что аналогичные меры приняты, чтобы быть уверенным в том, что сетевые ресурсы, представляемые сокетом, будут правильно очищены. Для этого используется блок try-finally.

Сокет производит «посвященную» связь, которая остается постоянной до тех пор, пока не будет явного рассоединения. (Посвященная связь может быть рассоединена неявно, если одна из сторон, или посредническая связь соединения рушатся.) Это означает, что два партнера замкнуты в коммуникации и соединение постоянно открыто. Это выглядит, как логический подход к сети, но это вносит дополнительную нагрузку на сеть. Позднее в этой главе вы увидите отличный подход к сетевому взаимодействию, при котором соединение является временным.

Обслуживание множества клиентов

JabberServer работает, но он может обработать только одного клиента одновременно. В обычных серверах вы захотите, чтобы была возможность иметь дело со многими клиентами одновременно. Ответом является многопоточность, и в языках, которые не поддерживают многопоточность напрямую, это означает что вы встретите все возможные трудности. В Главе 14 вы видели, что многопоточность в Java проста насколько это возможно, учитывая это, можно сказать, что многопоточность весьма сложная тема. Поскольку нити (потоки) в Java достаточно прамолинейны, то создание сервера, который обрабатывает несколько клиентов, относительно простое заняте.

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

В следующем коде сервера вы можете видеть, что он очень похож на пример JabberServer.java, за исключением того, что все операции по обслуживанию определенного клиента былы помещены внутрь отдельного thread-класса:

//: c15:MultiJabberServer.java
// Сервер, который использует многопоточность
// для обработки любого числа клиентов.
//
import java.io.*;

class ServeOneJabber extends Thread <
private Socket socket;
private BufferedReader in;
private PrintWriter out;

public ServeOneJabber ( Socket s ) throws IOException <
socket = s;
in = new BufferedReader ( new InputStreamReader ( socket.getInputStream ())) ;
// Включаем автоматическое выталкивание:
out = new PrintWriter ( new BufferedWriter ( new OutputStreamWriter ( socket
.getOutputStream ())) , true ) ;
// Если любой из вышеприведенных вызовов приведет к
// возникновению исключения, то вызывающий отвечает за
// закрытие сокета. В противном случае, нить
// закроет его.
start () ; // вызываем run()
>

public void run () <
try <
while ( true ) <
String str = in.readLine () ;
if ( str.equals ( «END» ))
break ;
System.out.println ( «Echoing: » + str ) ;
out.println ( str ) ;
>
System.out.println ( «closing. » ) ;
>
catch ( IOException e ) <
System.err.println ( «IO Exception» ) ;
>
finally <
try <
socket.close () ;
>
catch ( IOException e ) <
System.err.println ( «Socket not closed» ) ;
>
>
>
>

public class MultiJabberServer <
static final int PORT = 8080 ;

public static void main ( String [] args ) throws IOException <
ServerSocket s = new ServerSocket ( PORT ) ;
System.out.println ( «Server Started» ) ;
try <
while ( true ) <
// Блокируется до возникновения нового соединения:
Socket socket = s.accept () ;
try <
new ServeOneJabber ( socket ) ;
>
catch ( IOException e ) <
// Если завершится неудачей, закрывается сокет,
// в противном случае, нить закроет его:
socket.close () ;
>
>
>
finally <
s.close () ;
>
>
> // /:

Нить ServeOneJabber принимает объект Socket’а, который производится методом accept( ) в main( ) при каждом новом соединении с клиентом. Затем, как и прежде, с помощью Socket, создается BufferedReader и PrintWriter с возможностью автоматического выталкивания буфера. И наконец, вызывается специальный метод нити start( ). Здесь выполняются те же действия, что и в предыдущем примере: читается что-то из сокета и затем отсылается обратно до тех пор, пока не будет прочитан специальный сигнал «END».

Ответственность за очистку сокета должна быть, опять таки, внимательно спланирована. В этом случае, сокет создается вне ServeOneJabber, так что ответственность может быть совместная. Если конструктор ServeOneJabber завершится неудачей, он просто выбросит исключение тому, кто его вызвал, и кто должен очистить нить. Но если конструктор завершился успешно, то объект ServeOneJabber принимает ответственность за очистку нити на себя, в своем методе run( ).

Обратите внимание на упрощенность MultiJabberServer. Как и прежде создается ServerSocket и вызывается метод accept( ), чтобы позволить новое соединение. Но в это время возвращаемое значение метода accept( ) (сокет) передается в конструктор для ServeOneJabber, который создает новую нить для обработки этого соединения. Когда соединение завершиется, нить просто умирает.

Если создание ServerSocket’а проваливается, то из метода main( ), как и прежде, выбрасывается исключение. Но если создание завершается успешно, внешний блок try-finally гарантирует очистку. Внутренний try-catch гарантирует только от сбоев в конструкторе ServeOneJabber. Если конструктор завершится успешно, то нить ServeOneJabber закроет соответствующий сокет.

Для проверки этого сервера, который реально обрабатывает несколько клиентов, приведенная ниже программа создает несколько клиентов (используя нити), которые соединяются с одним и тем же сервером. Максимальное допустимое число нитей определяется переменной final int MAX_THREADS.

//: c15:MultiJabberClient.java
// Клиент, который проверяет MultiJabberServer,
// запуская несколько клиентов.
//
import java.net.*;

class JabberClientThread extends Thread <
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private static int counter = 0 ;
private int > private static int threadcount = 0 ;

public static int threadCount () <
return threadcount;
>

public JabberClientThread ( InetAddress addr ) <
System.out.println ( «Making client » + id ) ;
threadcount++;
try <
socket = new Socket ( addr, MultiJabberServer.PORT ) ;
>
catch ( IOException e ) <
System.err.println ( «Socket failed» ) ;
// Если создание сокета провалилось,
// ничего ненужно чистить.
>
try <
in = new BufferedReader ( new InputStreamReader ( socket
.getInputStream ())) ;
// Включаем автоматическое выталкивание:
out = new PrintWriter ( new BufferedWriter ( new OutputStreamWriter (
socket.getOutputStream ())) , true ) ;
start () ;
>
catch ( IOException e ) <
// Сокет должен быть закрыт при любой
// ошибке, кроме ошибки конструктора сокета:
try <
socket.close () ;
>
catch ( IOException e2 ) <
System.err.println ( «Socket not closed» ) ;
>
>
// В противном случае сокет будет закрыт
// в методе run() нити.
>

public void run () <
try <
for ( int i = 0 ; i 25 ; i++ ) <
out.println ( «Client » + id + «: » + i ) ;
String str = in.readLine () ;
System.out.println ( str ) ;
>
out.println ( «END» ) ;
>
catch ( IOException e ) <
System.err.println ( «IO Exception» ) ;
>
finally <
// Всегда закрывает:
try <
socket.close () ;
>
catch ( IOException e ) <
System.err.println ( «Socket not closed» ) ;
>
threadcount—; // Завершаем эту нить
>
>
>

public class MultiJabberClient <
static final int MAX_THREADS = 40 ;

public static void main ( String [] args ) throws IOException,
InterruptedException <
InetAddress addr = InetAddress.getByName ( null ) ;
while ( true ) <
if ( JabberClientThread.threadCount () )
new JabberClientThread ( addr ) ;
Thread.currentThread () .sleep ( 100 ) ;
>
>
> // /:

Конструктор JabberClientThread принимает InetAddress и использует его для открытия сокета. Вероятно, вы заметили шаблон: сокет всегда используется для создания определенного рода объектов Reader’а и Writer’а (или InputStream и/или OutputStream), которые являются тем единственным путем, которым может быть использован сокет. (Вы можете, конечно, написать класс или два для автоматизации этого процесса вместо набора этого текста, если вас это беспокоит.) Далее, start( ) выполняет инициализацию нити и запуск run( ). Здесь сообщение посылается на сервер, а информация с сервера отображается на экране. Однако, нить имеет ограниченноен время жизни и, в конечном счете, завершается. Обратите внимание, что сокет очищается, если конструктор завершился неудачей после создания сокета, но перед тем, как конструктор завершится. В противном случае, ответственность за вызов close( ) для сокета ложиться на метод run( ).

Threadcount хранит информацию о том, сколько в настоящее время существует объектов JabberClientThread. Эта переменная инкрементируется, как часть конструктора и декрементируется при выходе из метода run( ) (что означает, что нить умерла). В методе MultiJabberClient.main( ) вы можете видеть, что количество нитей проверяется, и если их много, то нить более не создается. Затем метод засыпает. Таким образом, некоторые нити, в конечном счете, умрут, и другие будут созданы. Вы можете поэкспериментировать с MAX_THREADS, чтобы увидеть, когда ваша конкретная система почувствует затруднения со множеством соединений.


Дейтаграммы

Пример, который вы недавно видели, использует Transmission Control Protocol (TCP, также известный, как сокет, основанный на потоках), который предназначен для наибольшей надежности и гарантии, что данные будут доставлены. Он позволяет передавать повторно потерянные данные, он обеспечивает множественные пути через различные маршрутизаторы в случае, если один из них отвалится, а байты будут доставлены в том порядке, в котором они посланы. Весь этот контроль и надежность добавляют накладные расходы: TCP сильно перегружен.

Существует второй потокол, называемый User Datagram Protocol (UDP), который не гарантирует, что пакет будет доставлен и не гарантирует, что пакеты достигнут точки назначения в том же порядке, в котором они были отправлены. Он называется «ненадежным протоколом» (TCP является «надежным протоколом»), что звучит плохо, но так как он намного быстрее, он может быть полезнее. Существуют приложения, такие как аудио сигнал, в которых не критично, если несколько пакетов потеряются здесь или там, а скорость жизненно необходима. Или например сервер времени, для которого реально не имеет значения, если одно из сообщений будет потеряно. Также, некоторые приложения могут быть способны отправлять UDP сообщения к серверу и затем считать, если нет ответа в разумный период времени, что сообщения были потеряны.

Обычно вы будете выполнять ваше прямое сетевое программирование с помощью TCP, и только иногда вы будете использовать UDP. Есть более общее толкование UDP, включая пример, в первой редакции этой книги (доступра на CR-ROM’е, сопровождающем это книгу или может быть свободно загружено с www.BruceEckel.com).

Использование URL’ов из апплета

Для апплета есть возможность стать причиной отображения любого URL с помощью Web броузера, в котором запущен апплет. Вы можете сделать это с помощью следующей строки:

в которой u является объектом типа URL. Вот простой пример, который перенаправляет вас на другую страницу. Хотя вы просто перенаправляете на HTML страницу, вы можете также перенаправить на вывод, который дает CGI программа.

public class ShowHTML extends JApplet <
JButton send = new JButton ( «Go» ) ;
JLabel l = new JLabel () ;

public void init () <
Container cp = getContentPane () ;
cp.setLayout ( new FlowLayout ()) ;
send.addActionListener ( new Al ()) ;
cp.add ( send ) ;
cp.add ( l ) ;
>

class Al implements ActionListener <
public void actionPerformed ( ActionEvent ae ) <
try <
// Это может быть CGI программа вместо
// HTML страницы.
URL u = new URL ( getDocumentBase () , «FetcherFrame.html» ) ;
// Отображается вывод URL с помощью
// Web броузера, как обычная страница:
getAppletContext () .showDocument ( u ) ;
>
catch ( Exception e ) <
l.setText ( e.toString ()) ;
>
>
>

public static void main ( String [] args ) <
Console.run ( new ShowHTML () , 100 , 50 ) ;
>
> // /:

Красота класса URL состоит в том, что он отлично защищает вас. Вы можете соединится с Web серверами без знания многого из того, что происходит за занавесом.

Чтение файла с сервера

Вариация приведенной выше программы, читающей файл, расположенный на сервере. В этом случае файл указывается клиентом:

public class Fetcher extends JApplet <
JButton fetchIt = new JButton ( «Fetch the Data» ) ;
JTextField f = new JTextField ( «Fetcher.java» , 20 ) ;
JTextArea t = new JTextArea ( 10 , 40 ) ;

public void init () <
Container cp = getContentPane () ;
cp.setLayout ( new FlowLayout ()) ;
fetchIt.addActionListener ( new FetchL ()) ;
cp.add ( new JScrollPane ( t )) ;
cp.add ( f ) ;
cp.add ( fetchIt ) ;
>

public class FetchL implements ActionListener <
public void actionPerformed ( ActionEvent e ) <
try <
URL url = new URL ( getDocumentBase () , f.getText ()) ;
t.setText ( url + «n» ) ;
InputStream is = url.openStream () ;
BufferedReader in = new BufferedReader (
new InputStreamReader ( is )) ;
String line;
while (( line = in.readLine ()) != null )
t.append ( line + «n» ) ;
>
catch ( Exception ex ) <
t.append ( ex.toString ()) ;
>
>
>

public static void main ( String [] args ) <
Console.run ( new Fetcher () , 500 , 300 ) ;
>
> // /:

Создание объекта URL похоже на предыдущий пример — getDocumentBase( ) является начальной точкой, как и прежде, но в то же время, имя файла читается из JTextField. Как только объект URL создан, его строковая версия помещается в JTextArea, так что вы можем видеть, как он выглядит. Затем из URL’а получается InputStream, который в данном случае может просто производить поток символов из файла. После конвертации в Reader и буферизации, каждая строка читается и добавляется в JTextArea. Обратите внимание, что JTextArea помещается внутрь JScrollPane, так что скроллирование обрабатывается автоматически.

Мультиплексирование, Основанное на Переключении в JDK 1.4

Когда вы читаете из сокета или пишете в него, вам нужно сделать передачу данных рациональной. Давайте рассмотрим сначала операцию записи. Когда вы пишите данные на уровне приложения (TCP или UDP сокет), вы пишите данные в рабочий буфер системы. Эти данные, в конечном счете, формируют (TCP или UDP) пакеты, которые необходимо передать на машину назначения по сети. Когда вы пишите в сокет и, если в буфере нет достаточно доступного места, запись может блокироваться. Если вы читаете из сокета и нет достаточного количества информации для чтения из буфера операционной системы, куда попадают данные после получения из сети, чтение будет блокировано. Если есть нить (поток) для операции чтения или записи, эта нить не может делать ничего и может стать причиной снижения произовдительности вашей программы. До появления JDK 1.4 не было способа вывести такую нить из заблокированного состояния. С помощью каналов вы можете выполнить асинхронную операцию закрытия на канале и нить, блокированная на этом канале примет AsynchronousCloseException.

Асинхронный ввод-вывод в Java достигается тем же способом, который дает вызов метода select( ) в UNIX подобных системах. Вы можете дать список дескрипторов (чтения или записи) в функцию select( ) и она отследит этот дескриптор на возникновение некоторых событий. Для дескриптора, представляющего сокет, из которого вы читаете, данные в буфере операционной системы для этого буфера представляются событием. Для дескрипторов, представляющих сокеты, в которые вы пишите, наличие места для записи во внутреннем буфере операционной системы для этого сокета представляется событием. Поэтому вызов метода select( ) исследует различные дескрипторы для проверки событий.

Что, если вы просто читаете и пишите в дескриптор когда бы вы не захотели? Select может обрабатывать множество дескрипторов, что позволит вам мониторить множество сокетов. Рассмотрим пример чат-сервера, когда сервер имеет соединения с различными клиентами. Тип данных, достигающих сервера, перемежается. Сервер предназначен для чтения данных из сокета и отображения их в GUI, то есть для показа каждому клиенту — чтобы достич этого, вы читаете данные от каджого клиента и пишите эти данные всем остальным клиентам. Например 5 клиентов: 1, 2, 3, 4 и 5. Если сервер запрограммирован на выполнение чтения от 1 и записи в 2, 3, 4 и 5, затем происходит чтения от 2 и запись в 1, 3, 4, 5 и так далее, то может так случиться, что пока нить сервера заблокирована на чтении одного из клиентских сокетов, могут появиться данные на других сокетах. Одно из решений состоит в том, чтобы создавать различные нити для кадого клиента (до JDK1.4). Но это не масштабируемое решение. Вместо этого вы можете иметь селектор, основанный на механизме, следящем за всеми клиентскими сокетами. Он знает какой сокет имеет данные для чтения без блокирования. Но если единственная нить выполняет эту работу (выбор и запись каждому клиенту) он не будет хорошо откликаться. Таким образом в таких ситуациях одна нить мониторит сокеты на чтение, выбирает сокет, из которого можно осуществить чтение, и делегирует остальную ответственность (запись другим клиентам) другой нити (нитям) или пулу нитей.

Этот шаблон называется шаблоном реактора, когда события отсоединяются от действия, ассоциированного с событиями (Pattern Oriented Software Architecture — Doug Schmidt).

В JDK 1.4 вы создаете канал, регестрируете объект Селектора в канале, который (объект) будет следить за событиями в канале. Многие каналы регестрируют один и тот же объект Селектора. Единственная нить, которая вызывает Selector.select(), наблюдает множество каналов. Каждый из классов ServerSocket, Socket и DatagramSocket имеют метод getChannel( ), но он возвращает null за исключением того случая, когда канал создается с помощью вызова метода open( ) (DatagramChannel.open( ), SocketChannel.open( ), ServerSocketChannel.open( )). Вам необходимо ассоциировать сокет с этим каналом.

Вы мультиплексируете несколько каналов (то есть сокеты), используя Селектор. Статический вызов Selector.select( ) блокирует выполнение до возникновения события в одном из каналов. Существует так же и не блокирующая версия этого метода, которая принимает количество милисекунд для засыпания или блокирования до того момента, когда вызов метода завершится.

ByteBuffer используется для копирования данных из канала и в канал. ByteBuffer является потоком октетов и вы декодируете этот поток, как символы. Со стороны клиента в MultiJabberClient.java это выполняется путем использования классов Writer’а и OutputStreamWriter’а. Эти классы конвертируют символы в поток байтов.

Приведенная ниже программа NonBlockingIO.java объясняет, как вы можете использовать Селектор и Канал для выполнения мультиплексирования. Эта программа требует запущенного Сервера. Она может стать причиной исключения на сервере, но ее назначение не в коммуникации с сервером, а в том, чтобы показать, как работает select( ).

//: TIEJ:X1:NonBlockingIO.java
// Сокет и Селектор сконфигурированы для не блокированного
// Соединения с JabberServer.java
//
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import java.io.*;

/**
* Цель: Показать как использовать селектор. Нет чтения/записи, просто
* показывается готовность к совершению операции.
*
* Алгоритм: -> Создаем селектор. -> Создаем канал -> Связываем сокет,
* ассоциированный с каналом, с -> Конфигурируем канал, как
* не блокирующий -> Регестрируем канал в селекторе. -> Вызываем метод select( ),
* чтобы он блокировал выполнение до тех пор, пока канал не будет готов. (как
* это предполагается методом select(long timeout) -> Получаем множество ключей,
* относящихся к готовому каналу для работы, основной интерес состоит в том,
* когда они зарегестрированя с помощью селектора. -> Перебираем ключи. -> Для
* каждого ключа проверяем, что соответствующий канал готов к работе, в которой
* он заинтересован. -> Если он готов, печатаем сообщение о готовности.
*
* Примечание: -> Необходим запущенный MultiJabberServer на локальной машине. Вы
* запускаете его и соединяетесь с локальным MultiJabberServer -> Он может стать
* причиной исключения в MultiJabberServer, но это исключение ожидаемо.
*/
public class NonBlockingIO <
public static void main ( String [] args ) throws IOException <
if ( args.length 2 ) <
System.out.println ( «Usage: java » ) ;
System.exit ( 1 ) ;
>
int cPort = Integer.parseInt ( args [ 0 ]) ;
int sPort = Integer.parseInt ( args [ 1 ]) ;
SocketChannel ch = SocketChannel.open () ;
Selector sel = Selector.open () ;
try <
ch.socket () .bind ( new InetSocketAddress ( cPort )) ;
ch.configureBlocking ( false ) ;
// Канал заинтересован в выполнении чтения/записи/соединении
ch.register ( sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE
| SelectionKey.OP_CONNECT ) ;
// Разблокируем, когда готовы к чтению/записи/соединению
sel.select () ;
// Ключи, относящиеся к готовому каналу, канал заинтересован
// в работе, которая может быть выполненаin can be
// без блокирования.
Iterator it = sel.selectedKeys () .iterator () ;
while ( it.hasNext ()) <
SelectionKey key = ( SelectionKey ) it.next () ;
it.remove () ;
// Если связанный с ключом канал готов к соединению?
// if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) <
if ( key.isConnectable ()) <
InetAddress ad = InetAddress.getLocalHost () ;
System.out.println ( «Connect will not block» ) ;
// Вы должны проверить возвращаемое значение,
// чтобы убедиться, что он соединен. Этот не блокированный
// вызов может вернуться без соединения, когда
// нет сервера, к которому вы пробуете подключиться
// Поэтому вы вызываете finishConnect(), который завершает
// операцию соединения.
if ( !ch.connect ( new InetSocketAddress ( ad, sPort )))
ch.finishConnect () ;
>
// Если канал, связанный с ключом, готов к чтению?
// if((key.readyOps() & SelectionKey.OP_READ) != 0)
if ( key.isReadable ())
System.out.println ( «Read will not block» ) ;
// Готов ли канал, связанный с ключом, к записи?
// if((key.readyOps() & SelectionKey.OP_WRITE) != 0)
if ( key.isWritable ())
System.out.println ( «Write will not block» ) ;
>
>
finally <
ch.close () ;
sel.close () ;
>
>
> // /:

Как указано выше, вам необходимо создать канал, используя вызов метода open( ). SocketChannel.open( ) создает канал. Так как он наследован от AbstractSelectableChannel (DatagramChannel и SocketChannel), он имеет функциональность для регистрации себя в селекторе. Вызов метода регистрации совершает это. В качестве аргумента он принимает Селектор для регистрации канала, и события, которые интересны для этого канала. Здесь показано, что SocketChannel заинтересован в соединении, чтении и записи — поэтому в вызове метода регистрации указано SelectionKey.OP_CONNECT, SelectionKey.OP_READ и SelectionKey.OP_WRITE наряду с Селектором.

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

Следующий пример работает так же, как и JabberClient1.java, но использует Селектор.

//: TIEJ:X1:JabberClient1.java
// Очень простой клиент, которй просто посылает строки на сервер
// и читает строки, посылаемые сервером.
//
import java.net.*;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class JabberClient1 <
public static void main ( String [] args ) throws IOException <
if ( args.length 1 ) <
System.out.println ( «Usage: java JabberClient1 » ) ;
System.exit ( 1 ) ;
>
int clPrt = Integer.parseInt ( args [ 0 ]) ;
SocketChannel sc = SocketChannel.open () ;
Selector sel = Selector.open () ;
try <
sc.configureBlocking ( false ) ;
sc.socket () .bind ( new InetSocketAddress ( clPrt )) ;
sc.register ( sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE
| SelectionKey.OP_CONNECT ) ;
int i = 0 ;
// По причине ассинхронной природы, вы не знаете
// когда чтение и запись закончены, поэтому вам необходимо
// следить за этим, переменная boolean written используется для
// переключения между чтением и записью. Во время записи
// отосланные назад символы должны быть прочитаны.
// Переменная boolean done используется для проверки, когда нужно
// прервать цикл.
boolean written = false, done = false ;
// JabberServer.java, которому этот клиент подсоединяется, пишет с
// помощью
// BufferedWriter.println(). Этот метод выполняет
// перекодировку в соответствии с кодовой страницей по умолчанию
String encoding = System.getProperty ( «file.encoding» ) ;
Charset cs = Charset.forName ( encoding ) ;
ByteBuffer buf = ByteBuffer.allocate ( 16 ) ;
while ( !done ) <
sel.select () ;
Iterator it = sel.selectedKeys () .iterator () ;
while ( it.hasNext ()) <
SelectionKey key = ( SelectionKey ) it.next () ;
it.remove () ;
sc = ( SocketChannel ) key.channel () ;
if ( key.isConnectable () && !sc.isConnected ()) <
InetAddress addr = InetAddress.getByName ( null ) ;
boolean success = sc.connect ( new InetSocketAddress (
addr, JabberServer.PORT )) ;
if ( !success )
sc.finishConnect () ;
>
if ( key.isReadable () && written ) <
if ( sc.read (( ByteBuffer ) buf.clear ()) > 0 ) <
written = false ;
String response = cs
.decode (( ByteBuffer ) buf.flip ()) .toString () ;
System.out.print ( response ) ;
if ( response.indexOf ( «END» ) != — 1 )
done = true ;
>
>
if ( key.isWritable () && !written ) <
if ( i 10 )
sc.write ( ByteBuffer.wrap ( new String ( «howdy » + i
+ ‘n’ ) .getBytes ())) ;
else if ( i == 10 )
sc.write ( ByteBuffer.wrap ( new String ( «ENDn» )
.getBytes ())) ;
written = true ;
i++;
>
>
>
>
finally <
sc.close () ;
sel.close () ;
>
>
> // /:

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

//: TIEJ:X1:MultiJabberServer1.java
// Имеет туж е семантику, что и многопоточный
// MultiJabberServer
//
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;

/**
* Сервер принимает соединения не блокирующим способом. Когда соединение
* установлено, создается сокет, который регистрируется с селектором для
* чтения/записи. Чтение/запись выполняется над этим сокетом, когда селектор
* разблокируется. Эта программа работает точно так же, как и MultiJabberServer.
*/
public class MultiJabberServer1 <
public static final int PORT = 8080 ;

public static void main ( String [] args ) throws IOException <
// Канал будет читать данные в ByteBuffer, посылаемые
// методом PrintWriter.println(). Декодирование этого потока
// байт требует кодовой страницы для кодировки по умолчанию.
String encoding = System.getProperty ( «file.encoding» ) ;
// Инициализируем здесь, так как мы не хотим создавать новый
// экземпляр кодировки каждый раз, когда это необходимо
// Charset cs = Charset.forName(
// System.getProperty(«file.encoding»));
Charset cs = Charset.forName ( encoding ) ;
ByteBuffer buffer = ByteBuffer.allocate ( 16 ) ;
SocketChannel ch = null ;
ServerSocketChannel ssc = ServerSocketChannel.open () ;
Selector sel = Selector.open () ;
try <
ssc.configureBlocking ( false ) ;
// Локальныйы адрес, на котором он будет слушать соединения
// Примечание: Socket.getChannel() возвращает null, если с ним не
// ассоциирован канал, как показано ниже.
// т.е выражение (ssc.socket().getChannel() != null) справедливо
ssc.socket () .bind ( new InetSocketAddress ( PORT )) ;
// Канал заинтересован в событиях OP_ACCEPT
SelectionKey key = ssc.register ( sel, SelectionKey.OP_ACCEPT ) ;
System.out.println ( «Server on port: » + PORT ) ;
while ( true ) <
sel.select () ;
Iterator it = sel.selectedKeys () .iterator () ;
while ( it.hasNext ()) <
SelectionKey skey = ( SelectionKey ) it.next () ;
it.remove () ;
if ( skey.isAcceptable ()) <
ch = ssc.accept () ;
System.out.println ( «Accepted connection from:»
+ ch.socket ()) ;
ch.configureBlocking ( false ) ;
ch.register ( sel, SelectionKey.OP_READ ) ;
>
else <
// Обратите внимание, что не выполняется проверка, если
// в канал
// можно писать или читать — для упрощения.
ch = ( SocketChannel ) skey.channel () ;
ch.read ( buffer ) ;
CharBuffer cb = cs.decode (( ByteBuffer ) buffer.flip ()) ;
String response = cb.toString () ;
System.out.print ( «Echoing : » + response ) ;
ch.write (( ByteBuffer ) buffer.rewind ()) ;
if ( response.indexOf ( «END» ) != — 1 )
ch.close () ;
buffer.clear () ;
>
>
>
>
finally <
if ( ch != null )
ch.close () ;
ssc.close () ;
sel.close () ;
>
>
> // /:

Здесь приведена простейшая реализация Пула Нитей. В этой реализации нет полинга (занят-ожидает) нитей. Она полностью основана на методах wait( ) и notify( ).

//: TIEJ:X1:Worker.java
// Instances of Worker are pooled in threadpool
//
//
import java.io.*;
import java.util.logging.*;

public class Worker extends Thread <
public static final Logger logger = Logger.getLogger ( «Worker» ) ;
private String workerId;
private Runnable task;
// Необходима ссылка на пул нитей в котором существует нить, чтобы
// нить могла добавить себя в пул нитей по завершению работы.
private ThreadPool threadpool;
static <
try <
logger.setUseParentHandlers ( false ) ;
FileHandler ferr = new FileHandler ( «WorkerErr.log» ) ;
ferr.setFormatter ( new SimpleFormatter ()) ;
logger.addHandler ( ferr ) ;
>
catch ( IOException e ) <
System.out.println ( «Logger not initialized..» ) ;
>
>

public Worker ( String id, ThreadPool pool ) <
worker > threadpool = pool;
start () ;
>

// ThreadPool, когда ставит в расписание задачу, использует этот метод
// для делегирования задачи Worker-нити. Кроме того для установки
// задачи (типа Runnable) он также переключает ожидающий метод
// run() на начало выполнения задачи.
public void setTask ( Runnable t ) <
task = t;
synchronized ( this ) <
notify () ;
>
>

public void run () <
try <
while ( !threadpool.isStopped ()) <
synchronized ( this ) <
if ( task != null ) <
try <
task.run () ; // Запускаем задачу
>
catch ( Exception e ) <
logger.log ( Level.SEVERE,
«Exception in source Runnable task» , e ) ;
>
// Возвращает себя в пул нитей
threadpool.putWorker ( this ) ;
>
wait () ;
>
>
System.out.println ( this + » Stopped» ) ;
>
catch ( InterruptedException e ) <
throw new RuntimeException ( e ) ;
>
>

public String toString () <
return «Worker : » + workerId;
>
> // /:

Основной алгоритм:
while true:

  1. Проверить очередь задач.
  2. Если она пуста, подождать, пока в очередь будет добавлена задача.
    (вызов метода addTask( ) добавляет задачу и уведомляет очередь для разблокирования)
  3. Пробуем получить рабочую (Worker) нить из пула нитей.
  4. Если нет ни одной доступной нити, ожидаем в пуле нитей.
    (Когда нить освободится, она уведомит пул нитей для разблокировки)
  5. На этой стадии есть задачи в очереди и есть свободная рабочая нить.
  6. Делегируем задачу из очереди рабочей нити.

//: TIEJ:X1:ThreadPool.java
// Пул нитей, которые выполняют задачи.
//
import java.util.*;

public class ThreadPool extends Thread <
private static final int DEFAULT_NUM_WORKERS = 5 ;
private LinkedList workerPool = new LinkedList () ,
taskList = new LinkedList () ;
private boolean stopped = false ;

public ThreadPool () <
this ( DEFAULT_NUM_WORKERS ) ;
>

public ThreadPool ( int numOfWorkers ) <
for ( int i = 0 ; i )
workerPool.add ( new Worker ( «» + i, this )) ;
start () ;
>

public void run () <
try <
while ( !stopped ) <
if ( taskList.isEmpty ()) <
synchronized ( taskQueue ) <
// Если очередь пустая, подождать, пока будет добавлена
// задача
taskList.wait () ;
>
>
else if ( workerPool.isEmpty ()) <
synchronized ( workerPool ) <
// Если нет рабочих нитей, подождать, пока
// пока не появится
workerPool.wait () ;
>
>
// Запускаем следующую задачу из расписания задач
getWorker () .setTask (( Runnable ) taskList.removeLast ()) ;
>
>
catch ( InterruptedException e ) <
throw new RuntimeException ( e ) ;
>
>

public void addTask ( Runnable task ) <
taskList.addFirst ( task ) ;
synchronized ( taskList ) <
taskList.notify () ; // Если добавлена новая задача, уведомляем
>
>

public void putWorker ( Worker worker ) <
workerPool.addFirst ( worker ) ;
// Здесь может быть случай, когда вы будете иметь пул из 5 нитей,
// а будет требоваться больше. Это происходит тогда, когда требуется
// рабочая нить,
// но ее нет (свободной), тогда просто блокируем пул нитей.
// Это событие, при котором появляется свободная рабочая нить в пуле
// нитей
// Поэтому эта нить посылает уведомление и разблокирует
// нить ThreadPool, ожидающую пул нитей
synchronized ( workerPool ) <
workerPool.notify () ;
>
>


private Worker getWorker () <
return ( Worer ) workerPool.removeLast () ;
>

public boolean isStopped () <
return stopped;
>

public void stopThreads () <
stopped = true ;
Iterator it = workerPool.iterator () ;
while ( it.hasNext ()) <
Worker w = ( Worker ) it.next () ;
synchronized ( w ) <
w.notify () ;
>
>
> // Junit test

public void testThreadPool () <
ThreadPool tp = new ThreadPool () ;
for ( int i = 0 ; i 10 ; i++ ) <
tp.addTask ( new Runnable () <
public void run () <
System.out.println ( «A» ) ;
>
>) ;
>
tp.stopThreads () ;
>
> // /:

Следующий пример MultiJabberServer2.java использует пул нитей. Это шаблон Реактора. Как установлено выше, события отделяются от ассоциированных с ними действий. Пул нитей ассинхронно разделяет действия, ассоциированные с событиями. В системах масштаба предприятия такое разделение обычно достигается путем использования Системы Cообщений Java — Java Messaging System (JMS).

//: TIEJ:X1:MultiJabberServer2.java
// Семантика аналогична MultiJabberServer1, с использованием пула нитей.
//
import java.io.*;

class ServeOneJabber implements Runnable <
private SocketChannel channel;
private Selector sel;

public ServeOneJabber ( SocketChannel ch ) throws IOException <
channel = ch;
sel = Selector.open () ;
>

public void run () <
ByteBuffer buffer = ByteBuffer.allocate ( 16 ) ;
boolean read = false, done = false ;
String response = null ;
try <
channel.register ( sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE ) ;
while ( !done ) <
sel.select () ;
Iterator it = sel.selectedKeys () .iterator () ;
while ( it.hasNext ()) <
SelectionKey key = ( SelectionKey ) it.next () ;
it.remove () ;
if ( key.isReadable () && !read ) <
if ( channel.read ( buffer ) > 0 )
read = true ;
CharBuffer cb = MultiJabberServer2.CS
.decode (( ByteBuffer ) buffer.flip ()) ;
response = cb.toString () ;
>
if ( key.isWritable () && read ) <
System.out.print ( «Echoing : » + response ) ;
channel.write (( ByteBuffer ) buffer.rewind ()) ;
if ( response.indexOf ( «END» ) != — 1 )
done = true ;
buffer.clear () ;
read = false ;
>
>
>
>
catch ( IOException e ) <
// будет поймано Worker.java и залогировано.
// Необходимо выбросить исключение времени выполнения, так как мы не
// можем
// оставить IOException
throw new RuntimeException ( e ) ;
>
finally <
try <
channel.close () ;
>
catch ( IOException e ) <
System.out.println ( «Channel not closed.» ) ;
// Выбрасываем это, чтобы рабочая нить могла залогировать.
throw new RuntimeException ( e ) ;
>
>
>
>

public class MultiJabberServer2 <
public static final int PORT = 8080 ;
private static String encoding = System.getProperty ( «file.encoding» ) ;
public static final Charset CS = Charset.forName ( encoding ) ;
// Создаем пул нитей с 20 рабочими нитями.
private static ThreadPool pool = new ThreadPool ( 20 ) ;

public static void main ( String [] args ) throws IOException <
ServerSocketChannel ssc = ServerSocketChannel.open () ;
Selector sel = Selector.open () ;
try <
ssc.configureBlocking ( false ) ;
ssc.socket () .bind ( new InetSocketAddress ( PORT )) ;
SelectionKey key = ssc.register ( sel, SelectionKey.OP_ACCEPT ) ;
System.out.println ( «Server on port: » + PORT ) ;
while ( true ) <
sel.select () ;
Iterator it = sel.selectedKeys () .iterator () ;
while ( it.hasNext ()) <
SelectionKey skey = ( SelectionKey ) it.next () ;
it.remove () ;
if ( skey.isAcceptable ()) <
SocketChannel channel = ssc.accept () ;
System.out.println ( «Accepted connection from:»
+ channel.socket ()) ;
channel.configureBlocking ( false ) ;
// Отделяем события и ассоциированное действие
pool.addTask ( new ServeOneJabber ( channel )) ;
>
>
>
>
finally <
ssc.close () ;
sel.close () ;
>
>
> // /:

Это минимальное обновления для JabberServer.java. Изначально, когда клиент посылает ‘END’, JabberServer не отправляет его назад. Эта версия JabberServer отсылает строку ‘END’ назад. Эти изменения были сделаны, чтобы упростить JabberClient1.java.

//: TIEJ:X1:JabberServer.java
// Очень простой сервер, который просто
// отсылает назад то, что получил от клиента.
//
import java.io.*;

public class JabberServer <
// Выбираем порт за пределами диапазона 1-1024:
public static final int PORT = 8080 ;

public static void main ( String [] args ) throws IOException <
ServerSocket s = new ServerSocket ( PORT ) ;
System.out.println ( «Started: » + s ) ;
try <
// Блокируем до возникновения соединения:
Socket socket = s.accept () ;
try <
System.out.println ( «Connection accepted: » + socket ) ;
BufferedReader in = new BufferedReader ( new InputStreamReader (
socket.getInputStream ())) ;
// Вывод автоматически выталкивается PrintWriter’ом:
BufferedWriter out = new BufferedWriter ( new OutputStreamWriter (
socket.getOutputStream ())) ;
while ( true ) <
String str = in.readLine () ;
System.out.println ( «Echoing: » + str ) ;
out.write ( str, 0 , str.length ()) ;
out.newLine () ;
out.flush () ;
if ( str.equals ( «END» ))
break ;
>
// Всегда закрываем два сокета.
>
finally <
System.out.println ( «closing. » ) ;
socket.close () ;
>
>
finally <
s.close () ;
>
>
> // /:

Еще о работе с сетью

На самом деле есть очень много тем, касающихся сетевой работы, которые могут быть освещены в этой вводной статье. Сетевая работа Java также предоставляет четкую и всестороннюю поддержку для URL, включая обработчики протоколов для различных типов содержимого, которое может быть доступно на Интернет сайте. Вы можете найти полное и подробное описание других особенностей сетевого взаимодействия Java в книге Elliotte Rusty Harold «Java Network Programming» (O’Reilly, 1997).

Задание 2 Messenger

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

Название ветки для разработки [вашгитхабаккаунт]-messenger

  • взаимодействие сервер-клиент происходит с помощью сообщений, с каждым действием связано соответствующее сообщение (логин, логаут, добавление в чат и т д). Сообщение инкапсулирует в себе все необходимы данные для обработки.
  • мессенджер поддерживает обработку сообщений от пользователя в специальном формате, начинающемся с /
  • в случае ошибки обработки сообщения, сервер возвращает сервисное сообщение со статусом ошибки (неправильные аргументы, не залогинен и т д)

Ниже описан консольный интерфейс команд. Для кодирования типа сообщения используем enum Type, общий предок всех сообщений в системе Message

У каждой команды со стороны клиента есть текстовое название (для консольного интерфейса)

показать список команд и общий хэлп по месседжеру. Реализуется полностью на клиенте

залогиниться (если логин не указан, то авторизоваться). В случае успеха приходит вся инфа о пользователе

получить всю информацию о пользователе, без аргументов — о себе (только для залогиненных пользователей)

получить список чатов пользователя(только для залогиненных пользователей). От сервера приходит список id чатов

/chat_create 1,2,3,4 — создать чат с пользователями >

/chat_create 3 — создать чат с пользователем >

создать новый чат, список пользователей приглашенных в чат (только для залогиненных пользователей).

/chat_history 2 — сообщения из чата >

список сообщений из указанного чата (только для залогиненных пользователей)

/text 3 Hello, it’s pizza time! — отправить указанное сообщение в чат >

отправить сообщение в заданный чат, чат должен быть в списке чатов пользователя (только для залогиненных пользователей)

Для каждой команды в системе должен существовать обработчик — реализация интерфейса Command, будет полезно разобраться с паттерном Команда вики

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

В рамках проекта будем использовать сокеты (java.net.Socket & java.net.ServerSocket). Необходимо обеспечить асинхронное взаимодействие между клиентом и сервером. Сервер должен уметь обрабатывать несколько соединений одновременно. На каждое новое подключение создается объект сессии arhangel.dim.core.net.Session, который инкапсулирует в себе информацию о клиенте и in/out каналы сокета для чтения и записи данных. Сессия должна реализовывать интерфейс

Чтобы передавать по сети сложные данные (объекты, коллекции) нужно разработать протокол общения. Протокол может быть текстовым или бинарным. Текстовый протокол проще для отладки, можно использовать формат json для представления объекта в виде строки. Итоговая строка конвертируется в byte[] — байтовый массив и отправляется в сокет. Бинарый протокол лучше, с точки зрения производительности и трафика, но сложнее в отладке. можно разработать свой протокол, можно воспользоваться встроенным механизмом сериализации.

Интерфейс протокол описан в arhangel.dim.core.net.Protocol (Для выполнения задания реализуйте BinaryProtocol в том же пакете)

Материалы по сериализации

Сериализация через сокет выглядит очень просто, достаточно обернуть stream

На стороне сервера можно выделить следующие сущности —

  • User(пользователь)
  • Message(Сообщение)
  • Chat(Разговор, чат)

У каждой сущности есть уникальный идентификатор (Long id). Данные на сервере хранятся в хранилище (базе данных), за взаимодействие с бд отвечают интерфейсы UserStore (пользователи) и MessageStore (сообщения и чаты). С помощью этих интерфейсов можно получить объекты из БД.

Примерная диаграмма классов

Примерная схема потока данных (данные идут как по стрелкам клиент-сервер, так и в обратную сторону)

Общение пользователей происходит в чатах. Чат — это разговор 2х и более пользователей. Каждый залогиненый пользователь может создать чат командой /chat_create , где — это список участников чата.

При попытке создать диалог (2 участника), возвращается существующий чат (как личные сообщения в соц сетях). При создании мультичата (> 2 участников) — создается новый, даже если существует чат с таким же набором участников.

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

В качестве утилиты для запуска используем написанный ранее контейнер. Можно посмотреть реализацию клиента client.xml

Для сервера в конфиге должны быть описаны компоненты, которые существуют в единственном экземпляре — Protocol, UserStore, MessageStore, Server. В конфигурацию нужно вынести все настройки, такие как порт, адрес БД, логин/пароль пользователя БД — то есть все, что можно сконфигурить.

Код должен состоять из независимых модулей, общающихся друг с другом через интерфейсы. Модуль должен быть вынесен в отдельный package. Не забывайте, что при хорошем ООП дизайне, каждый класс должен решать определенную задачу и, желательно, только её.

Также большое внимание уделите оформлению кода и его чистоте: Code Style Guide Полный гайд от гугла на англ. Некоторые рекоммендации на рус.

В коде нужно правильно обрабатывать исключительные ситуации и уметь объяснить, почему обработка происходит таким образом. Чтобы определиться с исключениями, подумайте какую задачу вы решаете и как должна себя вести система в том или ином случае. Исключение — это обработка именно нештатных ситуаций, неожидаемое поведение.

Для обработки и хранения данных правильно используйте коллекции. Нужно уметь объяснить свой выбор той или иной структуры данных, знать основные свойства (контракт, скорость доступа, добавление, удаление элементов).

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


Большое внимание уделите работе с потоками и корректной их остановке.

Итого, на оценку влияет

  • архитектура приложения (разделение на модули и классы, распределение ответсвенности между ними)
  • использование интерфейсов и наследования
  • ошибки разработчика (незакрытые ресурсы, необработанные исключения, неправильная проверка условий)
  • выбор способа хранения данных (правильно ли выбран коллекция для задачи, правильно ли выбран алгоритм, структура таблиц базы данных)
  • читаемость кода и его оформление

Задание разбиваем на 2 части — прототип и полная версия.

Минимум, который должен работать, иначе не начинаем смотреть задачу.

Включаем сервер, запускаем 2 клиент, один создает чат со вторым и отправляет сообщение. Второй клиент видит пришедшее сообщение. Сообщение сохранилось в базе. Не падает от невалидного ввода пользователя, уметь обрабатывать исключительные ситуации (Случаи, когда можно упасть — невалидный конфиг запуска, не работает сеть);

уметь обрабатывать подключение от нескольких клиентов в разных потоках; обрабатывать сообщения асинхронно;

создавать/логинить пользователя, сохранять информацию о нем в базе;

отправлять текстовое сообщение на сервер. Сервер в свою очередь отправляет это сообщение всем в чате

использовать для запуска утилиту container (напишите конфиг в файле server.xml);

реализуйте бизнес логику в соответствие с интерфейсами UserStore/MessageStore, работу с JDBC — подключение, получение Connection, вынесите в отдельный класс, чтобы не смешивать с логикой обработки данных. Изучите паттерны DAO/QueryExecutor и используйте один из них;

используйте PreparedStatement где возможно;

в прототипе еще можно использовать StringProtocol.

Реализовать все пары сообщение-команда из списка выше.

Для преобразования Message Object -> byte[] и обратно использовать сериализация (Java/JSON serialization)

Для отладочной информации использовать логирование (библиотека log4j например)

Написать юнит тесты на логику команд и на протокол

Использовать ConnectionPool к базе данных

Для управления потоками на сервере использовать ThreadPool

Как искать Java-разработчика: пособие для начинающих

Я занимаюсь подбором ИТ-специалистов более 7 лет, за это время я получила обширную экспертизу в этой области. Думаю, данный материал будет интересен как рекрутерам с опытом работы с данными специалистами, так и тем, кто только знакомится с миром ИТ, поэтому информацию постараюсь дать максимально просто.

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

Java-программист или Java-разработчик (не путать с JavaScript) занимается разработкой программного обеспечения (ПО) обычно в команде программистов. Написанный программный код программист сохраняет в специальном хранилище, которое называется системой контроля версий (Git и тп) . Кроме того, он должен исправлять свои ошибки в написанном коде, уведомление о которых поступает ему от тестировщиков.

Хороший программист (если мы нанимаем специалиста с опытом) имеет знания и практический опыт в технологиях Java, пишет качественный код, допускает не критичное количество ошибок, постоянно в курсе новых технологий и методов разработки, потому что он непрерывно занимается самообразованием.

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

Особенности:

  1. Айтишники в основном более интровертны и обычно любят говорить по делу. Предпочитают общаться по электронной почте и не любят, когда им звонят. Отчасти потому, что квалицированному специалисту постоянно названивают рекрутеры и отвлекают от работы, отчасти потому что они лучше воспринимают информацию и гораздо эффективнее общаются по переписке.
  2. Программист обычно сосредоточен на конкретной задаче, над которой он работает не отвлекаясь.
  3. Уровень интеллекта у айтишников в среднем достаточно высок, так как они получили хорошее математическое образование.
  4. Айтишники любят более неформальное общение и неформальный подход к работе.
  5. Они в основном совы и предпочитают работать с 11:00. Также многие достаточно свободолюбивы, не любят дресс-код итд. В таких условиях они обычно достигают лучших результатов.

1. Какова ситуация спроса и предложения на кадровом рынке по этой вакансии, что важно знать о компании, вакансии и условиях работы, чтобы выполнить задачу корректно и в срок, о чем важно спросить работодателя.

На рынке ИТ, в том числе и Java-разработчиков, даже в кризис правила игры определяет соискатель. Предложений достаточно для того, чтобы средний кандидат мог найти работу за 1-2 недели, выбирая между несколькими предложениями о работе. В среднем, на одну вакансию в сфере ИТ претендует 3 кандидата (из исследования hh.ru). Тем не менее, Java не является редкой технологией, эта платформа достаточно популярна у программистов, поэтому найти нужное количество кандидатов для собеседования возможно, но потока по этой вакансии у вас не будет.

При снятиии потребности у заказчика (работодателя) необходимо сразу получить полное описание вакансии, так как составить её самостоятельно без технических знаний в данной сфере рекрутер не сможет.

Обычно требования к джависту состоят из следующих блоков:

– Требование к образованию – чаще всего необходим кандидат с высшим техническим образованием.

– Знание и опыт коммерческой разработки на языке Java (не менее определённого количества лет), то есть студенческие и личные проекты работодателя не интересует.

– Часто указывается знание паттернов проектирования, но не всегда. Обычно успешный разработчик их знает и умеет применять.

– Знание определённого набора фреймворков (Hibernate, Spring итп.) и библиотек (например, JQuery). В каждой компании они требуются свои. Важно в этом пункте сразу уточнить у работодателя, что является обязательным, а что будет плюсом.

– Знание баз данных, например – MS SQL, Oracle и так далее.

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

– Наличие сертификатов по технологиям Java и другим – требуется редко. Сертификаты в данной сфере не показатель профессионализма, более того, если кандидат в первую очередь хвастается ими, а не своим опытом и знаниями, это обычно тревожный звоночек.

Обязательно уточните у работодателя следующее:

  1. Какой график предусмотрен для кандидата, есть ли переработки, имеется ли возможность график сдвигать?
  2. Присутствует ли дресс-код и насколько строгий?
  3. Где будет располагаться рабочее место кандидата, будет ли это опен спейс или в компании кабинетная система, будет ли он находиться среди коллег-айтишников или среди коллег из других подразделений?
  4. В вопросе компенсации важно иметь четкое представление, что есть оклад, что есть премия и бонусы, белая ли зарплата.
  5. Обязательно узнать обо всех аспектах соц. пакета, он обычно у айтишников очень широкий: кроме ДМС это может быть компенсация фитнеса, бесплатная столовая, 100% оплата больничного, и даже игровые приставки в офисе.
  6. На каком проекте или каких проектах будет работать данный сотрудник, сколько человек в команде, и вообще, лучше честно и прямо спросить: «Чем эта вакансия может заинтересовать кандидата, что мы можем рассказать кандидату, чтобы он загорелся». Это очень хороший вопрос, и адекватный нанимающий менеджер охотно расскажет про все преимущества.
  7. Подразумевается ли профессиональный и карьерный рост на данной позиции?
  8. Какие знания и навыки из описания вакансии обязательные (без которых резюме не будут смотреть) и какие желательные. Например, «знание Java – от 2 лет опыта работы и знание Spring.MVC – обязательно”. Уточните у заказчика, может ли кандидат не указать каких-то из этих ключевых слов в резюме.

2. Как представлять вакансию соискателям (нюансы и правила создания презентации вакансии).

Здесь нужно исходить их трёх основных мотиваторов айтишника:

  1. Интересные проекты.
  2. Технологии, с которыми он будет работать (например, ему уже надоела та платформа, на которой у него большой опыт, но ему очень хочется работать только с Java).
  3. Повышение своего дохода. Этот пункт может быть заменен на комфортность работы (дорога до офиса, удобство графика, наличие столовой, хорошего места для питания, парковки) или команду (насколько хороший микроклимат в команде и насколько она профессиональная).

В описании вакансии для публикации на ресурсах и в соц сетях обязательно хорошо распишите условия, избегайте казённых и шаблонных фраз. В заголовке достаточно написать «Программист Java” или “Ведущий Java-разработчик».

Составьте шаблон письма кандидату, где вы представляетесь, сообщаете, что у вас есть позиция и сразу пишите, что это Java-программист, далее описание вакансии и информация о компании. И, конечно, здесь распишите все преимущества компании, вакансии и проекта, которые вы ранее получили у заказчика (см. выше).

Кандидату лучше сначала высылать письмо, и потом звонить: “Я высылала вам письмо с вакансией” работает лучше, чем внезапный звонок.

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

3. Где размещаться (источники поиска, в том числе специальные и нетривиальные).

По статистике всё-таки больше кандидатов на Java находится на hh.ru, а лучшие приходят по рекомендации коллег-программистов.

Источники:

  1. hh.ru (и активный , и пассивный поиск).
  2. LinkedIn (лучше заранее работать над своей сетью контактов в тех сферах, где вы ищете и будете искать людей).
  3. Рекомендации других сотрудников-айтишников.
  4. Профессиональные форумы sql.ru и rsdn.ru, vingrad. Периодически появляются новые, теряют актуальность старые. Поэтому постоянно ищите новые ресурсы.
  5. Другие источники: ITMozg, Brainstorage, Programmersforum, Javatalks, Github.
  6. Facebook, Twitter, Вконтакте, жж-сообщества по программированию.
  7. Корпоративный сайт компании или агентства.

4. Стратегия поиска (отклики, активный обзвон, соцсети, объявления и т.д.).

1. Размещение вакансии на hh.ru, сбор откликов и активный поиск.

Для эффективного поиска лучше создать автопоиск на основании ключевых слов, которые соответствуют обязательным требованиям заказчика, например если это опыт в Java и фреймворк Hibernate, наш запрос будет выглядеть как

Java and Hibernate

Количество лет опыта мы будем определять уже по резюме.

2. Публикация объявления о вакансии со ссылкой на описание в соцсетях и на LinkedIn. В зависимости от соцсети преамбула может быть более или менее формальной и должна быть привлекательной и интересной. Попросите своих знакомых сделать репост ваших публикаций.


3. Публикация объявления на профессиональных форумах. Обязательно сначала прочитайте правила форума и раздела «Работа» или «Вакансии». За невыполнение требований вас забанят, а вакансию удалят. Например, на sql.ru публикацию нельзя редактировать, обязательно нужно указать оклад, название компании или агентства и город.

5. Как проводить первичный отбор по телефону (важные правила переговоров и критерии отбора).

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

Также, ваша задача – максимально дружелюбно и лаконично передать основную информацию, чтобы привлечь кандидата и чтобы он согласился на встречу. Хорошо, если у вас на первом очном интервью сразу будет присутствовать технический специалист, обязательно в таком случае упоминайте, что интервью будет техническое. К HR-интервью и агентствам айтишники относятся с осторожностью и некой опаской, это факт.

6. Особенности очного общения и оценки кандидатов.

Для айтишников не рекомендуется использование психологических тестов, так как они во-первых, показывают свою невалидность, во-вторых, в данном сегменте рынка труда психологические тесты и вопросы могут вызвать негативную реакцию.

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

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

Если вы подбираете Java-программиста для компании-заказчика, вам нужно будет развёрнутое интервью, чтобы собрать необходимую информацию для заказчика о кандидате.

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

Обязательно давайте обратную связь кандидату по телефону или по электронной почте в любом случае.

6. Подготовка кандидатов к интервью в компании.

Здесь нет никаких особенностей, кандидата необходимо снабдить всей необходимой информацией о компании и условиях работы, так как технари на эти вопросы не всегда могут ответить кандидату. Очень желательно рекрутеру присутствовать хотя бы на первом интервью с нанимающим менеджером и техническим специалистом, даже несмотря на то, что 90% того, что будет обсуждаться, не-технарю будет непонятно. Это даст вам возможность скорректировать свой поиск и лучше подобрать хорошего кандидата данному заказчику.

Спасибо за внимание, я надеюсь, что поиск айтишника, если он вам предстоит, будет лёгким и интересным!

Java Developer

Java Developer – это специалист, основной задачей которого является создание сложных корпоративных систем для обработки данных. К таким системам можно отнести настольные приложения, веб-сервисы, облачные хранилища данных, веб-приложения, а также сложные банковские системы.

Java – объектно-ориентированный строго-типизированный язык программирования с С-подобным синтаксисом, который наиболее близок к C++ и C#. В Java есть как свои преимущества, так и недостатки. Учитывая тот факт, что для запуска Java-приложений используется специальная виртуальная машина, то приложения запускаются медленнее, чем в случае с C++, но эта виртуальная машина позволяет запускать приложения на той операционной системе, где она установлена, что обеспечивает независимость от платформы. Java имеет огромное количество библиотек и фреймворков, которые позволяют разрабатывать собственные приложения быстрее.

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

Java-разработчик должен обладать уверенными знаниями объектно-ориентированного программирования, уметь писать простой читабельный код с применением шаблонов проектирования, чтобы разработчик с которым он работает в команде мог разобраться в его коде, знать алгоритмы и структуры данных, так как для многопользовательского приложения важна скорость выполнения и приходится искать самый эффективный алгоритм, знать базы данных, так как очень часто приходится выполнять сложные запросы и выборки из нескольких таблиц. Java имеет множество библиотек и фреймворков. Разработчикам нужно знать их не только на уровне использования, но и понимать, как эти библиотеки и фреймворки устроены внутри.

Для легкого старта в изучении языка Java ITVDN рекомендует пройти курсы Java Starter и Java Essential. Java разработчику не обойтись без глубоких знаний баз данных (SQL), а также алгоритмов и шаблонов проектирования. После этого можно переходить к уровню Java Professional.

Требования к Java Developer:

  • Глубокие знания языка программирования Java
  • Уверенные знания ООП и шаблонов проектирования
  • Уверенные знания алгоритмов и структур данных
  • Знания баз данных (MySQL или PostgreSQL)
  • Знания JDBC и базовые знание Hibernate
  • Знания систем тестирования приложений (TDD)
  • Знание веб-сервисов таких как XML и JSON
  • Базовые знание фреймворка Spring
  • Английский язык на уровне чтения технической документации (углубленные знания будут преимуществом)

Java разработчик может занимать такие должности:

Trainee Java Developer
Junior/Middle/Senior Java Developer
Программист Java
Java Team Lead
Java Solution Architect
Lead Java Engineer

Приложение «Hello MySQL»

Автор: Павел Пушкарев , paulus (at) sqlinfo (dot) ru

Изучение каждого языка программирования по традиции начинается с примера, выводящего на экран фразу «Hello world». Эта статья посвящена практически той же цели — показать, как можно сделать простейшую выборку из MySQL с использованием разных популярных языков программирования.

Сразу хочу сделать оговорки по поводу того, что будет в примерах, а чего не будет, и почему я пишу именно так, а не иначе:

  • я буду стараться делать максимально короткие примеры, чтобы не захламлять код ненужным мусором;
  • тем не менее, я буду добавлять много комментариев, чтобы код был читаемым;
  • все исключения, которые могут возникнуть в процессе выполнения программ, я буду обрабатывать, даже если можно написать более короткий код, просто игнорируя их;
  • наверняка, многие программисты смогут написать лучше или лаконичнее, но моя задача состоит в том, чтобы показать, что код пишется просто на любом языке — было бы желание. Тем более, что TIMTOWTDI.

Во всех примерах будет решаться одна и та же простая задачка — вывести строки таблички mysql.user, соответствующие определенному имени пользователя, передаваемому приложению из командной строки. Из-за того, что имя передается пользователем, во всех примерах используется специальный код для того, чтобы избежать инъекций SQL.

В этой статье я рассмотрю три интерпретируемых языка: PHP, Perl и Python, а также три компилируемых языка: Java, C и C++.

Hello MySQL с использованием PHP

PHP — один из наиболее популярных сейчас языков, который поддерживается большинством веб-серверов. Для этого примера, однако, я буду использовать не серверную, а консольную версию PHP (для того, чтобы можно было передать параметр из командной строки).

В PHP используют или обычную библиотеку доступа к MySQL или mysqli. Я буду использовать вторую, так как она общается с MySQL по новому протоколу и позволяет, например, получать результаты из хранимых процедур. Также она позволяет использовать объектно-ориентированный интерфейс, который мне близок по духу.

// Проверить, есть ли аргумент приложения
if ( $argc != 2 ) die ( «USAGE: $argv[0] \n » ) ;

// Подключиться к базе
$mysqli = new mysqli ( «localhost» , «root» , «» , «mysql» ) ;
if ( $mysqli -> connect_error )
die ( $mysqli -> connect_error . » \n » ) ;

// Подготовить запрос
$user = $mysqli -> real_escape_string ( $argv [ 1 ] ) ;
if ( ! ( $result = $mysqli -> query ( «SELECT * FROM user WHERE user = ‘$user'» ) ) )
die ( $mysqli -> error . » \n » ) ;

// Вывести данные
while ( $row = $result -> fetch_row ( ) ) <
print ( join ( » \t » , $row ) . » \n » ) ;
>
?>

Надо сделать несколько замечаний по использованию этого кода. Во-первых, здесь я использую real_escape_string, который в обычном коде с mysqli не используется. В mysqli есть замечательный метод prepare(), с помощью которого можно подготовить выражение и автоматически экранировать пользовательский ввод:

К сожалению, этот способ не подходит в данном случае, потому что результирующее подготовленное выражение может отдавать результат только в привязанные переменные (в отличие от использованного нами $result, позволяющего вытаскивать строки массивом), то есть мы должны заранее знать список столбцов и привязать к нему наши переменные PHP. В случае с нашим большим количеством столбцов это не удобно.

Во-вторых, надо понимать, что $mysqli->connect_error содержала ошибку до версии PHP 5.2.9, поэтому следует использовать ее с осторожностью.

Hello MySQL с использованием Perl

Perl — замечательный язык для быстрой обработки текстовой информации. Для доступа к базам данных Perl использует обобщенный интерфейс DBI.

#! /usr/bin/perl
use strict;
use DBI;

# Проверить, что запустили с параметром
die ( «USAGE: $0 \n » ) unless ( @ARGV == 1 ) ;

# Отлавливать ошибки в конце
eval <
# Соединиться с базой, включить режим выхода при ошибках
my $dbh = DBI-> connect ( «DBI:mysql:host=localhost;database=mysql» , «root» , «» ,
< PrintError =>0 , RaiseError => 1 > ) ;

# Выполнить запрос
my $sth = $dbh -> selectall_arrayref ( «SELECT * FROM user WHERE user=?» , undef , $ARGV [ 0 ] ) ;

# Вывести данные
print ( join ( » \t » , @$_ ) . » \n » ) foreach ( @$sth ) ;
> ;

# Если случилась ошибка, показать ее
die ( «Got error: $@» ) if ( $@ ) ;

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

Hello MySQL с использованием Python

Python — современный быстрый язык сценариев, который имеет свою библиотеку для доступа к MySQL. Так же, как и Perl, он поддерживает исключения, и поэтому так же, как и в Perl, код его короток и читаем:

#! /usr/bin/env python
# encoding: utf8
import sys
import MySQLdb

# Проверить, что запущены с параметром
if len ( sys . argv ) != 2 :
print ( «USAGE: » + sys . argv [ 0 ] + » » )
sys . exit ( -1 )

# Обрабатывать ошибки в конце
try :
# Подключиться к базе данных
conn = MySQLdb. connect ( «localhost» , «root» , «» , «mysql» )

# Выполнить запрос
cursor = conn. cursor ( )
cursor. execute ( «SELECT * FROM user WHERE user = %s» , ( sys . argv [ 1 ] ) )
rows = cursor. fetchall ( )

# Вывести результат
for row in rows:
print ( » \t » . join ( [ str ( col ) for col in row ] ) )

except MySQLdb. Error , e:
print ( «Got error: %s» % ( e. args [ 1 ] ) )
sys . exit ( -1 )

Hello MySQL с использованием Java

Java — полноценный кроссплатформенный компилируемый язык, который поддерживает общение с MySQL через обобщенный интерфейс доступа к БД JDBC. Для того, чтобы работал пример, необходимо, чтобы на клиентском компьютере было установлен Connector/J.


import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class Dump <
public static void main ( String [ ] args ) <
// Проверить, что программа запущена с аргументом
if ( args. length 1 ) <
System . out . println ( «USAGE: java Dumper » ) ;
System . exit ( -1 ) ;
>

// Проверять ошибки в конце
try <
// Подключиться к базе
Connection conn = DriverManager . getConnection ( «jdbc:mysql://localhost/mysql?user=root» ) ;

// Подготовить и выполнить запрос
PreparedStatement stmt = conn. prepareStatement ( «SELECT * FROM user WHERE user=?» ) ;
stmt. setString ( 1 , args [ 0 ] ) ;
ResultSet rs = stmt. executeQuery ( ) ;

// Вывести данные
int columns = rs. getMetaData ( ) . getColumnCount ( ) ;
while ( rs. next ( ) ) <
for ( int i = 1 ; i ) <
System . out . print ( rs. getString ( i ) + » \t » ) ;
>
System . out . println ( «» ) ;
>

> catch ( Exception ex ) <
System . out . println ( «Got error: » + ex. getMessage ( ) ) ;
>
>
>

Для того, чтобы это приложение заработало, нужно его правильно скомпилировать и правильно запустить. С компиляцией всё происходит прямолинейно: достаточно выполнить команду javac Dumper.java, чтобы получить скомпилированный байткод приложения Dumper.class. Запуск же этого байткода требует явного указания пути к драйверу JDBC MySQL:

Hello MySQL с использованием С

Язык С — один из самых первых компилируемых языков, который сохранился до наших дней. В нем нет поддержки исключений, поэтому, так же, как и на PHP, приходится каждый вызов оборачивать в if().

Следующий код любезно предоставлен для этой статьи Даниилом Каменским, dkamenskiy (at) yandex (dot) ru.

#include
#include
#include
#include

/* Функция выводит ошибку MySQL */
void print_error ( const char * str )
<
fprintf ( stderr, «ERROR: %s \n » , str ? str: mysql_error ( &mysql ) ) ;
>

/* Основная функция */
int main ( int argc, char ** argv )
<
MYSQL_RES *mysqlres;
MYSQL_ROW row;
char *query;
char *tablename;
unsigned rows;
unsigned i;

/* Проверить, что программу запустили с параметром */
if ( argc != 2 ) <
fprintf ( stderr, «USAGE: %s username \n » , argv [ 0 ] ) ;
return -1 ;
>

/* Подключиться к MySQL с параметрами пользователя по умолчанию */
if ( !mysql_real_connect ( &mysql, «localhost» , «root» , «» , «mysql» , 0 , NULL , 0 ) ) <
print_error ( NULL ) ;
return -1 ;
>

/* Обработать параметр, чтобы не допустить SQL injection */
tablename = malloc ( strlen ( argv [ 1 ] ) * 2 + 1 ) ;
mysql_real_escape_string ( &mysql, tablename, argv [ 1 ] , strlen ( argv [ 1 ] ) ) ;

/* Составить запрос */
query = strdup ( «SELECT * FROM mysql.user WHERE user = ‘» ) ;
query = realloc ( query, strlen ( query ) + strlen ( tablename ) + 2 ) ;
strcat ( query, tablename ) ;
strcat ( query, «‘» ) ;

/* Освободить выделенную под составление запроса память */
free ( query ) ;
free ( tablename ) ;

/* Прочитать результат запроса */
if ( ! ( mysqlres = mysql_store_result ( &mysql ) ) ) <
print_error ( NULL ) ;
mysql_close ( &mysql ) ;
return -1 ;
>

/* Вывести результат запроса на экран */
rows = mysql_num_fields ( mysqlres ) ;
while ( ( row = mysql_fetch_row ( mysqlres ) ) ) <
for ( i = 0 ; i )
printf ( «%s \t » , row [ i ] ) ;
printf ( » \n » ) ;
>

/* Освободить результат запроса */
mysql_free_result ( mysqlres ) ;

/* Отключиться от MySQL */
mysql_close ( &mysql ) ;
return 0 ;
>

Написание программ на С — развлечение для настоящих программистов :) Нужно следить абсолютно за всем — начиная от проверок ошибок и заканчивая корректным освобождением объектов и закрытием всех соединений.

При написании программ на С можно или поступать так, как написано в этом примере, или эмулировать try-catch блоки с помощью оператора goto. Обычно этот оператор не рекомендуется использовать, но в данном случае он может значительно сократить объем кода, а главное улучшить его читаемость.

Для того, чтобы сделать из текста этого примера приложение, нужно его скомпилировать. Например, если Вы пользуетесь компилятором gcc, то нужно выполнить

Hello MySQL с использованием С++

Многие считают, что С++ — это расширенная версия С. По синтаксису — это почти так. Но у С++ совсем другая идеология, которую я постараюсь передать.

До недавнего времени, единственной хорошей библиотекой доступа была Connector/C++, которая работала полностью подражая JDBC. Следующий пример использует для своей работы, однако, библиотеку mysql++, которая появилась относительно недавно, но написана непосредственно для С++, а потому куда удобнее.

int main ( int argc, char * argv [ ] )
<
using namespace mysqlpp;

// Проверить, что программу запустили с параметром
if ( argc != 2 ) <
std:: cerr «USAGE: » [ 0 ] » » endl ;
return -1 ;
>

// Обрабатывать все ошибки в конце
try <
// Подключиться к MySQL
Connection conn ( «mysql» , «localhost» , «root» , «» ) ;

// Составить запрос
Query query = conn. query ( ) ;
query «SELECT * FROM mysql.user WHERE user = » [ 1 ] ;

// Выполнить запрос
StoreQueryResult res = query. store ( ) ;

// Вывести результат
for ( size_t row = 0 ; row num_rows ( ) ; ++row ) <
for ( size_t col = 0 ; col num_fields ( ) ; ++col )
std:: cout [ row ] [ col ] » \t » ;
std:: cout endl ;
>

Для того, чтобы скомпилировать это приложение, я использую следующую команду:

Заключение

Неужели досюда кто-то дочитал? :) В награду Вам ссылка на архив с исходными кодами всех описанных приложений. Желаю удачи в программировании!

Как проходит техническое собеседование на Java-разработчика

Зиновий Верес — Solution Architect в компании SoftServe, руководитель образовательного направления Lviv IT Cluster. За последние 10 лет провел более 300 технических собеседований с Java-специалистами разных уровней.

Как построен процесс технического интервьюирования изнутри, какие знания и методы помогут кандидатам успешнее справляться с заданиями и к каким рекомендациям прислушаться соискателям — Зиновий отвечает на главные вопросы и делится своим опытом.

Что такое техническое интервью

Техническое собеседование — это второй этап интервьюирования соискателя в компании. Проводит его уже не HR-специалист, а член команды программистов. В среднем общение длится 60-90 минут. За это время нужно достигнуть главной цели — выявить сильные стороны кандидата. Эта информация нужна не только техническому специалисту, но и рекрутеру компании. Ведь в дальнейшем он может предложить кандидату проект по его интересам или скиллам.

Цель технического интервью — не «завалить» кандидата, а определить уровень его знаний.

Первая часть интервью: опыт работы

Начало собеседования традиционное — пара отвлеченных вопросов, помогающих снять напряжение и познакомиться. Задача первого этапа интервью — узнать, как кандидат может работать в команде. Для этого, как говорит Зиновий, выясняется реальный опыт работы кандидата. Важно понять:

  1. Как была построена его предыдущая команда и какова была его роль.
  2. Как он сам оценивает свою роль на последнем проекте.
  3. Как команда взаимодействовала и согласовывала требования по задачам.
  4. Как команда рассчитывала длительность выполнения задач.
  5. Была ли у команды и у самого кандидата возможность влиять на сам процесс разработки.

Если собеседуют Java-специалиста на позицию Teach Lead`а или Senior, то обращают внимание на его административные способности: как он в предыдущих проектах организовывал работу над кодом, как велся проект с управленческой и технической сторон, и какие выводы в итоге сделал кандидат.

Обязательные вопросы:
1. Что, по вашему мнению, стоит изменить в проекте?
2. Какие из принятых вами решений оказались ошибочными и почему?

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

Вторая часть интервью: техническая

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

Первый этап — практическая часть с заданиями.

Здесь самый распространенный вариант — описать задачу и спросить у кандидата, как бы он ее решил. Например, у потенциального Tech Lead/Architect Зиновий спрашивает: как бы вы обеспечили одновременную поддержку 5-ти тысяч дополнительных пользователей для описанного вами проекта?

Второй этап — теоретическая часть с вопросами по объектно-ориентированному дизайну.

Естественно, кандидат должен знать Java и разбираться в изменениях, которые были внесены в Java SE 8/9 s Java EE 8, Jakarta EE. Также базовый набор вопросов касается SQL/DB Design, программного доступа к базе данных (DB Access) со знанием JPA/Hibernate.

Если ваш уровень Intermid и выше, вы обязательно должны знать, для чего нужно использовать шаблоны проектирования (GoF/GRASP/SOLID/Layered Architecture); какой шаблон использовать, а когда в нем вообще нет необходимости, и как делать рефакторинг кода.

Третий этап — вопросы по архитектуре.

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

  • Дайте полное описание архитектуры проекта: как вы его задокументировали и какие вносили изменения, в чем была их причина?
  • Какие существуют главные качественные характеристики системы (quality attributes)? Почему важны именно они? Как вы их достигали и какие допущения сделали при проектировании системы?
  • Как выбирали фреймворки для проекта, какие были альтернативы и что в итоге определило выбор?
  • С какими проблемами столкнулись, делая запрос в базу данных, и как их решали?
  • Как реализовывали REST-сервисы? С помощью какой библиотеки реализовывали HATEOAS-принцип?
  • Какой версией Java пользовались на проекте? Что из версии Java SE 8/9 планируете использовать?
  • Какие ваши познания в Java EE: что вы знаете о ее составе, что планируете использовать и как происходит ее релиз?
  • Если работали “Out of memory error”, то как решали и отслеживали использование ресурсов?
  • Если работали с “Memory leak”, то как искали причину и организовывали работу с памятью?


Зачем на интервью задают каверзные вопросы

Каверзные вопросы — это не способ вас «завалить». Наоборот — с их помощью можно узнать, как вы мыслите, реагируете на нестандартные ситуации и справляетесь с непростыми задачами. В целом, это такой прием, позволяющий увидеть ваши soft skills.

Как кандидату вести себя на интервью

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

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

Третье — просите фидбек после собеседования. Интервьюер сможет подробно рассказать вам, какие у вас сильные стороны, а над какими скиллами еще необходимо поработать.

И чисто психологический момент — будьте спокойными. Собеседование — это стресс для любого специалиста. А чтобы снизить его уровень, Зиновий советует дома узнать максимальное количество информации о компании, проработать ответы на часто задаваемые вопросы и определить свои главные стороны, о которых вы готовы рассказать.

P.S. С какими материалами ознакомиться при подготовке к собеседованию

Зиновий Верес рекомендует ознакомиться с двумя книгами: Джошуа Кериевски «Рефакторинг с использованием шаблонов» и Len Bass «Software Architecture in Practice».

Закрыть

Пройдите обучение с личным наставником и устройтесь на свою первую работу в IT

+7 (900) 621-50-31

Мы гарантируем трудоустройство по договору.
100% наших выпускников устраивается на работу.

Пройти 5 пробных дней

Почему стоит учить JAVA ?

Большие возможности для разработки Java

Высоконагруженные системы
(Google, Yandex, Facebook)

Банковские приложения
(Сбербанк, Альфа Банк, ВТБ 24)

Мобильные приложения
(Instagram, Telegram)

Искусственный интеллект
(Whatson, ViaVoice)

Интернет-магазины
(Ebay, Amazon)

Высоконагруженные системы ( Google, Yandex, Facebook)
Банковские приложения ( Сбербанк, Альфа Банк, ВТБ 24)
Мобильные приложения ( Instagram, Telegram)
Искусственный интеллект ( Watson, ViaVoice)
Интернет-магазины ( Ebay, Amazon)
Игры ( Minecraft)

JAVA — Самый востребованный
язык программирования

Стабильно занимает лидирующую позицию
по данным индекса TIOBE

Сколько можно зарабатывать?

Средняя зарплата ведущего Java — разработчика
200 000 рублей

Xxxxxx xxxx xxx xxxxxx xxxxx.

Xxxxx xxxxx xxxx xxx xxx xxxx

Как построено обучение в Java Mentor

Индивидуальная программа обучения

Неограниченное общение с ментором 6 дней в неделю

Мы гарантируем вам трудоустройство по договору

Работа над коммерческим, проектом

Основатель проекта Java Mentor

Наставник в собственном проекте Java Mеntor

▸ Победитель Всероссийского хакатона HackRussia с проектом OpenKnowledge – портал для дистанционного обучения

▸ Победитель на медицинском хакатоне ТилТехМедХак с проектом симптомчекер — «Наташа».

▸ Победитель хакатона Промсвязьбанка PSB Samara Battle с проектом «Виртуальный платёжный терминал прямо в мобильном телефоне»

▸ Победитель Всероссийского хакатона HackUniversity с проектом History Layers – система, которая позволяет увидеть, как мир вокруг нас выглядел в прошлом

▸ Победитель хакатона по технологиям в рекламе и маркетинге Adhack Galaxy

▸ Победитель Международного хакатона AngelHack
с проектом – сервис для борьбы с рекламой на асфальте

▸Победитель HR Hack Экспофорум с виртуальным помощником по подбору вакансий

▸ Работал С++ разработчиком, переквалифицировался на Java

Наши партнеры по трудоустройству

iSimpleLab — российский разработчик современной системы ДБО для физических и юридических лиц iSimpleBank 2.0

Рексофт — один из ведущих российских разработчиков программного обеспечения.
Работает в сфере IT-услуг с 1991 года, за это время успели выполнить более 1000 проектов в области IT-консалтинга, проектирования, разработки, тестирования и поддержки ПО

SEMrush- международная IT-компания, которая разрабатывает платформу для интернет-маркетологов. Продукт входит в ТОП-3 маркетинговых сервисов мира.

Месяц в подарок

При единовременной оплате
7 месяцев обучения, 8 месяц в подарок

84 000 рублей Вместо 96 000 рублей

Обучение с ментором — это возможность устроиться на первую работу Java – разработчиком в минимальный срок

Программа обучения

8 месяцев регулярных занятий
приведут вас к трудоустройству в IT

Java 0

≈ 1 Месяц

Получение базовых знаний, необходимых для работы с Java: знакомство с консолью, данными в Java, управляющими конструкциями

Тема 1 Знакомство с консолью: Вывод в консоль; ввод с клавиатуры

Тема 2 Знакомство с данными в Java: Начальные знания о переменных, начальные знания о классах и методах

Тема 3 Знакомство с управляющими конструкциями: Начальные знания об условиях, начальные знания о циклах

Java CORE

≈ 2 Месяца

Изучение основ Java :объектно-ориентированное программирование, создание простых программ

Тема 4 Введение в Java: История и описание Java, средства разработки Java (JDK); виртуальная машина Java (JVM); первая программа в консоли; первая программа в среде разработки.


Тема 5 Базовый синтаксис Java: Примитивные типы, классы-обертки; Преобразование типов; Ссылочные типы: строки и массивы, управляющие конструкции: условия и циклы.

Тема 6 ООП в Java: Основы ООП, пакеты, модификаторы доступа, классы, перечисления, аннотации,
наследование, класс Object, абстрактные классы и интерфейсы.

Тема 7 Исключения и логирование: Описание и иерархия исключений; Обработка и использование исключений; Логирование.

Тема 8 Потоки ввода-вывода, доступ к файловой системе: Работа с файловой системой; Потоки байт; Потоки символов; Внешние процессы; Сериализация.

Тема 9 Дополнительные возможности Java: Параметризованные типы; Java Collections; Функциональное программирование в Java;
Stream API.

Java web 1

≈ 0,5 Месяца

Изучение основ работы с сетью, принципов создания web-приложений, работа с базами данных, создание простых web-приложений

Тема 10 Знакомство с инструментами разработки: Maven; Git; Jetty; Freemarker;

Тема 11 Работа с сетью: WebSockets; Servlet API; HttpSession; Cookie

Тема 12 Работа с базами данных: Java Database Connectivity (JDBC); Object Relational Mapping (ORM); Persistency API (JPA); Hibernate; Data Access Object (DAO); Транзакции.

Java web 2

≈ 0,5 Месяца

Углубленное изучение работы web-приложений, их тестирования, работа с ресурсами, создание многопоточных web-приложений

Тема 13 Тестирование: Виды тестирования; Unit тестирование (JUnit); Дублёры (Mockito); Нагрузочное тестирование; JMX.

Тема 14 Работа с ресурсами: Десереализация; XML; SAX parser; DOM parser; Java NIO.

Тема 15 Многопоточность: Потоки; Взаимодействия потоков; Concurrent Collections.

≈ 5 МесяЦев

Подготовка к командному проекту

Подготовка к работе над коммерческим проектом в команде

Тема №1 Разработка web-сервиса с использованием технологий Tomcat, Servlet, JSP, Maven , JDBC + паттерн проектирования Executor.

ТЕМА №2 Добавление в проект JPA, Hibernate. Применение паттернов Factory, Singletone.

ТЕМА №3 Добавление RBAC (Role Based Access Control), разделение по ролям, контроль доступности ресурсов для администратора и пользователя. Использование технологии Servlet Filters.

ТЕМА №4 Знакомство с Spring Core, перевод проекта с Servlet на Spring MVC. Конфигурирование Spring для работы с ORM (Hibernate).

Задача №5 Подключение Spring Security вместо Servlet Filters. Настройка конфигурации безопасности web-приложения.

ТЕМА №6 Подключение фреймворка Bootstrap для создания адаптивных web страниц.

ТЕМА №7 Перевод проекта на Spring Boot с использованием шаблонизатора Thymeleaf.

ТЕМА №8 Создание RESTful сервиса с использованием технологий Spring REST. Построение грамотной REST архитектуры.

ТЕМА №9 Разработка клиента (пользователя) рестфул сервиса с использованием технологий Spring REST Template.

командный проект

Командное написание коммерческого проекта с использованием
всего стека технологий необходимых Java – разработчику

Разработка коммерческого решения для бизнеса в команде от 3 до 6 человек с TeamLead’ом по методологии SCRUM.Стек используемых технологий: Java Core, JSP или другой шаблонизатор (Freemarker/Thymeleaf), Servlet, SQL, Spring Core Spring MVC, Spring Security, JDBC, Hiberante/ Spring Data, HTML/CSS, JavaScript, Maven, Git. Опыт разработки этого проекта вы сможете указать как последнее место работы на позиции Java разработчика. Это создание НАСТОЯЩЕГО коммерческого продукта.

Резюме + Трудоустройство

≈ 1 Месяц

Подготовка конкурентного резюме, тестовые собеседования с ментором, подбор актуальных компаний и трудоустройство в одну из них

Почему обучение
с личным ментором эффективнее?

Java
mentor

Удаленное обучение из любого города

Много практических задач

Автоматическая проверка кода

Общение со студентами через чат

Ориентированность на результат – трудоустройство

Подробный разбор слабых сторон
и контроль усвоения материала

Ручная проверка кода и отработка
альтернативных путей решения

Работа над настоящим
коммерческим проектом в команде

Неограниченное общение
с каждым студентом голосом

Дмитрий
Шепелев

До встречи с ментором я уже программировал и имел опыт разработки на С#. Я решил переквалифицироваться на Java, чтобы найти работу, так как вакансий на С# было гораздо меньше. Вот я и начал изучать Java курсы, статьи, книги, очень много информации, но нельзя объять необъятное. Поэтому я решил, что нужен тот, кто уже прошёл этот путь и работает Java – разработчиком, и кто задаст мне ориентир, в какую сторону копать. И вот, я его нашёл!
Герман давал задания, пояснял их, объяснял как, что устроено. Я начал выполнять задания, набирался опыта и практики. Это было летом, поэтому будучи студентом, я мог сидеть сутками напролёт. В день я занимался по 8 часов. Получив задания, я сразу не задавал ментору вопрос, типа «как это сделать?», а сидел и разбирался сам. Ментор меня направлял так, чтобы я изучил те технологии, которые необходимо знать джуну на его первой работе. Я делал свой проект и продолжаю его делать, как известно, на работе мы не всегда изучаем что-то новое, мы решаем, прикладные проблемы, поэтому чтобы развиваться и «быть на плаву» нужны домашние проекты. Так вот, примерно, после 3 месяцев такого продуктивного развития, я уже начал искать работу. Кстати, в вопросе связанном с собеседованиями, Герман также не оказался в стороне. Он собеседовал меня сам, и это было очень полезно. Так вот, походив на собеседования, мне было предложено пару офферов на вакансии Java — разработчик. И вот, теперь я уже работаю в фирме программистом, за что хочу сказать своему Ментору огромное Спасибо!

Алексей
Сушков

Когда я решил учить Java, я около года потерял, пытаясь научиться чему-то самостоятельно, но это напоминало хождение по кругу. Я активно учил основы околомесяца, писал простенькую программу, потом про все забывал, а через месяц все начиналось заново.Я начал искать ментора, но понять может ли человек на самом деле из меня сделать разработчика или просто хочет срубить денег не было ясно. И вот в один из вечеров мне написал Герман, представился ментором и предложил созвониться. После беседы, я подумал, что стоит попробовать и не прогадал. Уже через неделю пробных занятий и постоянной помощи и советов не только Германа, но и таких же, как я ребят, я согласился поступить на обучение. Сама Java завлекла меня настолько, что иногда я проводил за задачами по 12 часов. Но обучение программированию — это не только выучить синтаксис и написать пару тройку тестовых заданий! Были моменты, когда Герман давал мне задания иногда совсем несвязанные с Java, но позволяющие получить другие полезные навыки, такие как: регулярные выражения, работа с API других Web-сервисов и многое другое.В те моменты, когда ты трое суток бьёшься над одной задачей, есть желание все бросить, решить, что это не твое и вообще разочароваться в себе. Именно тогда я понял, зачем мне нужен ментор. Он не просто расскажет в какой строчке кода ты ошибся, но и объяснит почему это так, даст пару советов и вернет желание изучать язык.Герман научил меня не просто программировать, он сделал меня разработчиком, за что я ему благодарен. 5 месяцев моего обучения были, пожалуй, самыми насыщенными и сложными, но теперь, уже работая разработчиком, я понимаю, что все сделал правильно.

Эдик
Грачев

Я давно задался целью — работать Java-dev. Читал статьи, проходил различные курсы. Но этого было так недостаточно, чтобы устроиться на работу! Мне не хватало человека, который поможет структурировать информацию и объяснить огромное количество нюансов. Тогда я нашёл Германа. С ним стало намного проще, а процесс обучения намного быстрее!

Станислав
Сорокин

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

Отзывы

Посмотреть видео отзывы

1/12 Юрий Бочкарев

2/12 Евгения Ковтун

3/12 Алексей Сушков

4/12 Станислав Сорокин

5/12 Кирилл Владимиров

6/12 Виталий Беляков

7/12 Артём

8/12 Дмитрий Зубов

9/12 Алексей Павленко

10/12 Александр Голуб

11/12 Соколов Сергей

12/12 Вячеслав Зоткин

Алексей Сушков

“ Когда я решил учить Java, я около года потерял, пытаясь научиться чему-то самостоятельно, но это напоминало хождение по кругу. Я активно учил основы околомесяца, писал простенькую программу, потом про все забывал, а через месяц все начиналось заново.Я начал искать ментора, но понять может ли человек на самом деле из меня сделать разработчика или просто хочет срубить денег не было ясно. И вот в один из вечеров мне написал Герман, представился ментором и предложил созвониться. После беседы, я подумал, что стоит попробовать и не прогадал. Уже через неделю пробных занятий и постоянной помощи и советов не только Германа, но и таких же, как я ребят, я согласился поступить на обучение. Сама Java завлекла меня настолько, что иногда я проводил за задачами по 12 часов. Но обучение программированию — это не только выучить синтаксис и написать пару тройку тестовых заданий! Были моменты, когда Герман давал мне задания иногда совсем несвязанные с Java, но позволяющие получить другие полезные навыки, такие как: регулярные выражения, работа с API других Web-сервисов и многое другое.В те моменты, когда ты трое суток бьёшься над одной задачей, есть желание все бросить, решить, что это не твое и вообще разочароваться в себе. Именно тогда я понял, зачем мне нужен ментор. Он не просто расскажет в какой строчке кода ты ошибся, но и объяснит почему это так, даст пару советов и вернет желание изучать язык.Герман научил меня не просто программировать, он сделал меня разработчиком, за что я ему благодарен. 5 месяцев моего обучения были, пожалуй, самыми насыщенными и сложными, но теперь, уже работая разработчиком, я понимаю, что все сделал правильно. „

Дмитрий Шепелев

До встречи с ментором я уже программировал и имел опыт разработки на С#. Я решил переквалифицироваться на Java, чтобы найти работу, так как вакансий на С# было гораздо меньше. Вот я и начал изучать Java курсы, статьи, книги, очень много информации, но нельзя объять необъятное. Поэтому я решил, что нужен тот, кто уже прошёл этот путь и работает Java – разработчиком, и кто задаст мне ориентир, в какую сторону копать. И вот, я его нашёл!
Герман давал задания, пояснял их, объяснял как, что устроено. Я начал выполнять задания, набирался опыта и практики. Это было летом, поэтому будучи студентом, я мог сидеть сутками напролёт. В день я занимался по 8 часов. Получив задания, я сразу не задавал ментору вопрос, типа «как это сделать?», а сидел и разбирался сам. Ментор меня направлял так, чтобы я изучил те технологии, которые необходимо знать джуну на его первой работе. Я делал свой проект и продолжаю его делать, как известно, на работе мы не всегда изучаем что-то новое, мы решаем, прикладные проблемы, поэтому чтобы развиваться и «быть на плаву» нужны домашние проекты. Так вот, примерно, после 3 месяцев такого продуктивного развития, я уже начал искать работу. Кстати, в вопросе связанном с собеседованиями, Герман также не оказался в стороне. Он собеседовал меня сам, и это было очень полезно. Так вот, походив на собеседования, мне было предложено пару офферов на вакансии Java — разработчик. И вот, теперь я уже работаю в фирме программистом, за что хочу сказать своему Ментору огромное Спасибо!

Эдик Грачев

“ Я давно задался целью — работать Java-dev. Читал статьи, проходил различные курсы. Но этого было так недостаточно, чтобы устроиться на работу! Мне не хватало человека, который поможет структурировать информацию и объяснить огромное количество нюансов. Тогда я нашёл Германа. С ним стало намного проще, а процесс обучения намного быстрее! „

Станислав Сорокин

“ Изучать программирование я решил после окончания института. Выбор пал на Java из-за большой популярности этого языка и высоких зарплат Java-программистов. Первое время учил самостоятельно, но из-за обилия информации, и, непонимания того, что мне нужно в итоге, обучение затягивалось и не приносило результатов. Поняв это, я начал искать ментора. Выбор пал на обучение у Германа по двум причинам: во-первых, из-за возможности постоянного контакта с ментором и контроля с его стороны за моим прогрессом, во-вторых, из-за того, что Герман ставит своей задачей именно устройство ученика на работу, а это то, что меня интересовало. Во время занятий с Германом я стал чувствовать, что занимаюсь именно тем, что мне нужно из-за четко выстроенного плана обучения, особенно мне понравилось работать на реальном проекте с другими учениками. После окончания проекта я почувствовал, что за это время стал настоящим программистом и без проблем смог устроиться на работу, как и хотел. Рекомендую всем обучение у Германа потому, что оно нацелено именно на становление ученика как java-программиста и устройство на первую работу. „

Цукерберг рекомендует:  Выпадающее меню на jQuery
Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих