Пример email-курса Java EE Hunter

HTTP 1.1 Methods

У HTTP три версии: 0.9 (она буквально через полгода-год была заменена), 1.0 (через пару лет вышла 1.1) и 1.1. И сейчас практически все браузеры и серверы поддерживают HTTP 1.1.

В версии 1.1 определено 8 методов HTTP: GETPOSTHEADOPTIONSPUTDELETETRACECONNECT. Самые популярные – GET и POST.

Есть группа HTTP-запросов Safe methods (HEAD, GET, OPTIONS, TRACE) – это такие запросы, что они вообще не должны менять состояние сервера (например, чтение главной страницы). Это означает, что если в течении какого-то небольшого времени был уже подобный запрос, то он может запомнить ответ и выдать его нам. То есть мы можем получить такой же ответ, который получил 30 секунд назад вообще другой пользователь. За счет этого, когда провайдер ставит хороший прокси, то мы провайдеру заплатили за 1Тб траффика, а он наружу вывел 0.5Тб траффика. Такое кеширование может происходить на очень многих уровнях – на сервере в виде прокси-сервера Nginx, у провайдера на прокси, в виде кеша в браузере. Таким образом при помощи управлением хеширования можно заметно снизить нагрузку на сеть и время отдачи.

Header для джавистов выглядит как Map<String, List<String>>. При этом ключ от значения отделен двоеточием, а значения (если их более одного) разделяются запятыми. Поэтому часто используют точку с запятой, если хотят две логически разных штуки слепить вместе – тогда HTTP будет считать, что это одно значение. Причем переносы строк определены в стандарте HTTP – пары ключ-значение должны разделяться переносом строки.

В Headers обычно указывается такая информация, типичный пример Headers:

GET / HTTP/1.1

Host: localhost

User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:36.0) Gecko/20100101 Firefox/36.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate

Connection: keep-alive

Параметрами q=0.9,*/*;q=0.8 задается приоритет – это относительные числа от 0 до 1.

Оптимизация. Connection: keep alive – это свойство называется persistent connection. Если клиент посылает Connection типа live, он предлагает не закрывать TCP Socket. Обычно мы делаем GET, получаем HTML, обнаруживаем там много изображений и посылаем одновременно много запросов. Когда мы первый раз делаем GET, мы открываем TCP соединение, обмениваемся техническими пакетами, посылаем пакет с данными HTTP, получаем ответ – TCP не закрывается. Нам очень удобно, что TCP не надо каждый раз открывать – TCP открывается достаточно медленно. Прежде, чем пойдет первый пакет с данными (первый пакет, в котором HTTP-шный GET), происходит обмен несколькими техническими пакетами. По аналогии, как JDBC драйвер перед запросом SELECT обменивается данными с БД.

Есть еще одна оптимизация, которая еще сильнее ускоряет, но которую поддерживают не все клиенты и не все серверы. Она называется – HTTP pipelining (поддерживается только в HTTP/1.1, не в 1.0.). Сейчас у нас клиент посылает запрос, сервер думает, присылает ответ, клиент посылает следующий запрос после того, как получил ответ на предыдущий. HTTP pipelining – это технология, которую должен поддерживать и клиент, и сервер. Она заключается в том, что клиент может открыть два потока OutputStream и InputStream и непрерывно послать на сервер много GET-запросов подряд и непрерывно читать ответы. Конечно возникает проблема ставить соответствие запросы-ответы. Итак, получается, что если нам нужно послать 2 запроса по 1 байту (один запрос-ответ – это 100 миллисекунд), мы потратим 200 мс, а так мы посылаем фактически двухбайтный запрос и получаем двухбайтный ответ. Обычно HTTP работает как пинг-понг (запрос-ответ, запрос-ответ), а HTTP pipelining – как пулемет (то есть мы отстреляли рожок, а потом слышим эхо).

HTTP pipelining и persistent connection – это оптимизации первого уровня, но их в общем-то не хватает. Сейчас очень популярны два протокола – WebSockets и SPDY. Они пытаются еще улучшить протокол HTTP.

Partial GET. Когда мы посылаем GET, мы можем указать GET и диапазон байтов, которые мы хотим взять. С этим можно было столкнуться во времена медленного интернета, когда были программы Downloaders (DownloadMaster, FlashGet). Можно было, посылая GET, указать диапазон байтов. Если надо было скачать файл 100 Мб, мы могли сделать 100 запросов: первый посылал GET с нулевого по миллионный байт, второй – с миллионного по двухмиллионный и т.д. Если рвалось соединение – можно было делать докачку.

Компрессия. В любом нормальном сервере есть опция сжимать все с помощью deflate или GZIP.

AJAX. Когда мы закачиваем HTML (это текстовый файл в некотором формате), то к нему можно прилепить еще 2 штуки: файл *.css и *.js.

*.css. В HTML и данные документа и стили оформления. Со временем развился CSS (Cascading Style Sheets) – каскадные таблицы стилей – и мы можем в отдельном файле описать стиль, в котором, например, сказать, что первая строка будет красной, а вторая – зеленой. Получается, что в HTML будут только данные, а в CSS – часть верстки, которая касается стилей. Меняем CSS – меняется внешний вид, данные те же самые. Картинки тоже считаются элементами оформления, в файле css описаны URLs на картинки. Один из ведущих сайтов по CSS – CSS Zen Garden.

*.js. В HTML можно подключать JavaScript. Сейчас все браузеры обязаны поддерживать JavaScript – это специфический совершенно отдельный язык программирования, который к Java вообще не имеет никакого отношения (в свое время они заплатили компании Sun, чтобы использовать слово Java – это было модное слово). Это не ООП язык, а object-based, у него наследование не на основе классов, а на основе прототипов, там не статическая типизация, а динамическая, там вообще нет понятия класса и экземпляра класса. Этот язык сейчас очень сильно набирает популярность. По мере того, как клиент становится очень жирным (типа Gmail, или онлайн-игр), где очень много происходит внутри браузера, становится нужен JavaScript. Может быть ситуация, что JavaScript будет рендерить всю страницу HTML. Когда HTML посылает запрос на сервер, назад приходит не HTML, а только сырые данные (какая-то Map или таблица), а в браузере на JavaScript эти данные вычитываются и строится очень сложный интерфейс (яркий пример — чат). У JavaScript нету метода main, потому что JS не главный – программа на JS запускается по какому-то событию (нажатию на какой-то элемент).

Внутри JS есть XMLHttpRequest, который позволяет из JS сделать отдельный запрос на сервер. Получается, когда мы загрузили браузером жирный сайт, браузер может делать запрос на сервер двумя разными способами. Первый – вверху есть поле ввода, мы вводим URL и нажимаем Enter, или есть гиперссылка. Главное здесь то, что мы нажимаем на ссылку, на сервер идет GET-запрос, а обратно идет новый HTML, браузер полностью перезагружается, рендерит новый HTML. Второй – можно средствами JS послать запрос, получить ответ с сервера в JS, браузер о нем не узнает, он не перезагрузит страницу, просто JS-программа получит данные. Дальше можно из JS менять HTML. HTML выстраивается в так называемую DOM-модель (Document Object Model), это в общем-то дерево.

Работа с кешомCachcontrol – очень рассогласованный, там несколько разных способов, которые друг другу противоречат. Он состоит в двух моментах: первый – клиент посылает GET-запрос на сервер, ему приходят данные, и там может быть указано, что этот файл актуален еще 24 часа. Если в течении 24 часов браузер захочет сделать запрос на этот же URL, он имеет право не делать этот запрос, а взять данные из локального кеша. Можно указать в Cache control вообще не кешировать страницу, тогда ее никто не кеширует – ни браузер, ни прокси по пути. Яркий пример – страница актуальных новостей, ее нельзя кешировать. Запросы, связанные с онлайн-играми. Клиент делает запрос, сервер рендерит ответ и указывает, можно ли это кешировать, если можно, то как долго. Второй момент – клиент может послать специальный запрос (GET с какими-то флагами), при котором он просит дать данные, если они изменились, например, за последний день. Это нужно, например, если сервер не знает, как долго должна жить картинка. Поэтому здесь идея такая, что клиент посылает запрос на картинку, если она изменилась за какое-то время. А сервер может прислать новую картинку или прислать специальный код, что картинка не изменилась.

Заливка на сервер. У нас есть GET, а есть POST. Когда мы делаем GET-запрос, у нас в запросе есть только Headers, но нет тела. Когда рендерим Response, у нас есть и Headers и тело (мы присылаем pdf, xml, html назад). Когда мы делаем POST-запрос, у нас есть и Headers, и тело (мы можем залить файлы на сервер при помощи POST-запроса).

Список HTTP headers. Список HTTP status codes.

MVC

MVC (Model-View-Controller, модель-вид-контроллер) – это схема использования нескольких шаблонов проектирования, с помощью который модель данных приложения, пользовательский интерфейс и взаимодействие с пользователем разделены на три отдельных компонента таким образом, чтобы модификация одного из компонентов оказывала минимальное воздействие на остальные. Книга GoF – не первоисточник шаблона MVC.

При разделении Servlet и JSP, Servlet – это controller, а JSP – view. Как только пытаешься написать минимальное приложение, которое эталонно использует Servlet и JSP, сразу же оказывается, что пишешь MVC. Эталонная работа с Servlet и JSP – это реализация шаблона MVC.

Идея MVC в том, что из интернета в наш контейнер (который сидит на TCP) приходят разные запросы, контейнер преобразовывает их в HTTP. Запрос попадает в Controller, после чего контроллер смотрит на запрос, разбирает его (запрос может быть очень сложным) и валидирует (смотрит, корректный ли запрос). После чего он обращается к какому-то layer (например, DAO), и оттуда получает Model (в нашем случае – отдельный Quiz). Имеется в виду, что Model – это экземпляры класса предметной области (какие-то товары, продукты, скидки).

Задача Controller – посмотреть, что входной запрос корректный (например, что там все параметры), узнать, откуда из бизнес-уровня достать кусок Model (может оказаться, что у него много DAO-шек и он должен знать, в какую сходить). Model– это все состояние интернет магазина: все товары, все запросы, вся история, все скидки.

Получив ответ, он должен решить, через какой View (их может быть много) рендерить ответ. Задача View – содержать дизайн.

Apache Struts и JSF построены на MVC. Это View фреймворки, которые отвечают за рендеринг, обычно это наборы библиотек без Servlet. Они включают компоненты View и Controller.

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

В модели MVC происходит redirect с Controller, который получил запрос, он по нему сходил куда-то в бизнес-слой, получил Model, а теперь он хочет сделать внутренний redirect на View. Он делает setRequestDispatcher(“quiz.jsp”)для имени этого View quiz.jsp, делаем ему forward() или include() и даем ему наш Request и Response. А к ним приделаны Map. Они для того и приделаны, чтобы мы могли туда положить свою модель.

View framework более высокого уровня, чем Servlet – это JSF (JavaServer Faces) или GWT (Google Web Toolkit). Это две технологии, который стоят над Servlet. А MVC – это ядро этих фреймворков, это архитектурный принцип, на котором они построены. И поэтому, когда мы рассматриваем MVC, мы понимаем, как в будущем будут эксплуатировать Servlets.

Чтобы вся спецификация не была таким странным набором фактов, желательно рассматривать архитектурные концепции более высокого уровня.

Continious Integration

Всякие сервера по автоматизированному построению проектов зачастую используют Maven.

Есть такой процесс – Continuous Integration (CI) – непрерывная интеграция. Она заключается в выполнении частых автоматизированных сборок проекта для скорейшего выявления и решения интеграционных проблем. В обычном проекте, где над разными частями системы разработчики трудятся независимо, стадия интеграции является заключительной. Она может непредсказуемо задержать окончание работ. Переход к непрерывной интеграции позволяет снизить трудоёмкость интеграции и сделать её более предсказуемой за счет наиболее раннего обнаружения и устранения ошибок и противоречий. Непрерывная интеграция является одним из основных приёмов экстремального программирования.

В общем-то она заключается в том, что у нас есть система контроля версий – SVN или GIT (интересная статья) На ней висит билд-сервер для обеспечения непрерывной интеграции – TeamCity или Hudson. И когда мы туда коммитим свежие src, билд-сервер их вытягивает и пробует все скомпилировать и запустить все тесты.

Если у нас там есть pom.xml, и у нас все заточено под Maven, то билд-сервер может тоже автоматически запустить Maven и все скомпилировать. И Maven либо ругнется, если что-то неправильно, либо скажет Build success. При этом билд-сервер может отправить письмо программисту если закомиченный код не скомпилировался.

Потом в Maven можно запустить все unit-тесты. В данном случае Maven не зависит от IDE, а является независимым инструментом командной строки.

JDBC Interfaces

Driver – это абстракция над тем переходником, который написал производитель MySQL.

Connection – это абстракция, которая соответствует одному физическому соединению с базой данных.

Statement – это SQL, который мы посылаем в базу данных.

ResultSet – это то реляционное отношение, которое нам вернулось – результат.

Как уже было указано, Connection – это пример паттерна Abstract Factory. Connection генерирует много интерфейсов, но нас интересуют из них триStatement (для обычных SQL), PreparedStatement (работает с прекомпилированным SQL) и CallableStatement (наш способ вызывать хранимую процедуру в базе данных).

Statement производит ResultSet, который работает по шаблону Iterator. Когда нам возвращается реляционное отношение, к нему доступ имеет ResultSet. У него есть метод boolean next(), который делает две вещи – переходит на следующую запись и возвращает true, если запись есть, и false, если записей больше нет.

Закрывать ресурсы рекомендуется в обратном порядке: сначала ResultSet, потом Statement и потом Connection.