В процессе автоматизации деятельности предприятия при помощи 1С: Предприятие часто
возникают задачи интеграции и обмена с оборудованием и другими сторонними информационными
системами, например, банками, веб-сайтами, информационными системами партнеров.
Традиционно 1С: Предприятие выступает в качестве потребителя услуг, и реже – в качестве
поставщика. До этого момента у разработчиков популярными технологиями при получении
информации от 1C были COM и веб-сервисы, которые появились только в версии 8.1.
У обеих технологий (COM и веб-сервисы) есть свои минусы. Главные недостатки заключаются
в следующем. COM-технология позволяет в каждый момент времени выполнять только один
запрос. Обращение возможно только внутри своей локальной сети. Обработка одновременных
запросов для COM возможна, но требует затрат на организацию пула соединений и отладку
многопоточного приложения. Веб-сервисы же сложны по настройке и негибкие для программирования:
жестко привязаны к SOAP-стандартам. Подключение устройств, обменивающихся простыми
пакетами, работающих по http-протоколу, невозможно.
Новая идея организации веб-сервера внутри 1С, предложенная в статье, опирается на
проверенную временем богатую функциональность .Net Framework. Решение на основе
идеи лишены недостатков COM и веб-сервисов 1С. По сравнению с COM http-сервер можно
использовать вне локальной сети, поддерживается одновременная обработка нескольких
запросов. По сравнению с веб-сервисами 1С решение на базе http-сервера обладает
большей гибкостью, так как программист волен сам выбирать формат ответа сервера
(в том числе HTML, JSON, графические изображения, RSS и т.д.), а также контролировать
при ответе URL-адрес, идентификацию пользователей, коды ошибок, куки, кодировку,
осуществлять кеширование. Настройка же http-сервера внутри 1С сводится к простому
запуску внешней обработки.
Описание примера
Пример, приложенный к статье, состоит из двух файлов: http-сервера (внешняя обработка
1С 8.2 HttpServer82) и тестового приложения эмуляции одновременных запросов к серверу
(внешняя обработка 1С 8.2 TestHttpServer82). Обе обработки выполнены на основе управляемых
форм. По умолчанию оба приложения настроены на работу с портом 8082.
Сервер и тестовое приложение разработаны на 1С: Предприятие 8.2 и используют .Net
Framework 4.0 и компонент Elisy .Net Bridge 4. Соответственно, для работы примера
требуется установленный .Net Framework 4.0 и версия библиотеки Elisy .Net Bridge
v.4.0.2.0 и выше. Elisy .Net Bridge позволяет гармонично использовать классы и технологии
.Net Framework на 1С, ведущую роль оставляя 1С.
Для проверки работы достаточно запустить внешнюю обработку HttpServer82.epf из 1С: Предприятие
8.2. Если у вас Windows с включенным UAC, то запустить 1С: Предприятие необходимо
под администратором, иначе у приложения не будет достаточно прав на прослушивание
запросов.
Внешняя обработка позволяет задать порт, по которому будет осуществляться прослушивание
и число создаваемых потоков для обработки одновременных запросов. По умолчанию установлен
порт 8082 и 50 потоков.
После нажатия на кнопку Старт сервер переходит в рабочее состояние и осуществляет
обработку запросов на заданный порт. Например, теперь можно обратиться из вашего
браузера по адресу localhost:8082 и открыть
страницу, которую вернет сервер. В запросе же можно передавать параметры, например,
так: localhost:8082/test?x=1
Для проверки сервера в многопоточном режиме придумана внешняя обработка TestHttpServer82.epf,
которая осуществляет одновременный запуск запросов в цикле. В основу обработки для
организации параллельной работы положена замечательная технология PLINQ (Parallel
LINQ) из .Net framework 4.
Запускать тестовую обработку TestHttpServer82.epf следует из отдельного от сервера
сеанса 1С: Предприятие, иначе одновременный запуск в одном сеансе двух обработок
приведет к зависанию. В качестве параметров тестового примера выступают адрес запроса,
число одновременных запросов и число циклов. По умолчанию запуск алгоритма приведет
к 3 циклам обращений по адресу localhost:8082
с 20 одновременными запросами (надо отметить, что число одновременных запросов ограничено
числом ядер процессора).
Принцип работы
.Net framework предлагает своим разработчикам класс HttpListener, отвечающий за прослушивание http-протокола. Используя HttpListener,
вы можете создать прослушивание http-трафика, которое отвечает на http-запросы.
Вы можете использовать этот класс только на операционных системах Windows XP SP2
или Windows Server 2003 и выше. Попытка использования класса на более ранних системах
вызовет исключение.
Ниже приведен пример кода для 1С, который инициализирует объект типа HttpListener,
настраивая его на прослушивание всех URL по порту 8082. При запуске в 1С работа
программы приостанавливается, пока не последует запрос на порт, например, из браузера.
Как только вы пошлете из браузера запрос, например, 127.0.0.1:8082/ 1С вернет описанную в программе html-строку.
За что ответственен каждый участок кода, можно понять из комментариев, приведенных
внутри исходного кода.
ПодключитьВнешнююКомпоненту("Elisy.NetBridge4");
AddIn = New("AddIn.ElisyNetBridge4");
net = AddIn.GetNet();
Если НЕ net.GetStatic("System.Net.HttpListener","IsSupported") Тогда
Сообщить("Для класса HttpListener нужна ОС Windows XP SP2/2003 и выше");
Возврат;
КонецЕсли;
listener = net.New("System.Net.HttpListener");
listener.Prefixes.Add("http://*:8082/");
listener.Start();
Сообщить("Прослушивание...");
//Метод GetContext блокирует выполнение программы пока ждет запрос.
context = listener.GetContext();
request = context.Request;
// Получить объект ответа
response = context.Response;
// Создать ответ - HTML-строку
responseString = "Ответ от HttpListener";
buffer = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(responseString);
// Получить поток ответа и записать ответ в него.
response.ContentLength64 = buffer.Length;
output = response.OutputStream;
output.Write(buffer,0,buffer.Length);
// Необходимо закрыть выходной поток и остановить прослушивание
output.Close();
listener.Stop();
Это самый простой пример. Главный смысл простейшего примера в том, что 1С превращается
в полнофункциональный http-сервер, а 1С-программист получает инструмент для гибкой
настройки ответа с сервера. Он может вернуть его в любом формате (html, рисунок
или JSON), затребовать идентификацию пользователя или вообще вернуть ошибку. Но
вместе с тем есть недостатки: блокирование всего приложения, обработка только одного
запроса. Пример к статье содержит более сложный код, за счет чего устранены недостатки
простейшего примера из листинга.
От простого к сложному
В готовый пример вошел видоизмененный код работы с HttpListener, в котором вызов
метода listener.GetContext() заменен вызовом асинхронных аналогов listener.BeginGetContext()
и listener.EndGetContext(). Кроме этого создаются N отдельных потоков и заложена
синхронизация между потоками с вызовом кода-обработки запроса на стороне 1С: Предприятие.
Достоинством предлагаемой реализации http-сервера является возможность одновременной
обработки N запросов в разных потоках с передачей логики обработки в метод формы
1С: Предприятие. Пример при каждом запросе возвращает html с описанием источника
и URL запроса. Решение очень гибкое, так как все доработки можно делать прямо из
конфигуратора 1С. Никакие дополнительные проекты (например, C# или VB.Net) не задействованы.
Пример условно можно разделить на 2 части: код, который выполняется на стороне 1С: Предприятие
и код, который выполняется на стороне .Net framework. При этом .Net framework взял
на себя все, что нельзя реализовать средствами 1С: Предприятие, например, создание
и синхронизацию потоков.
Http-сервер оформлен в виде управляемой формы. При нажатии на кнопку Старт происходит
создание объектов классов HttpServer и Helper. Оба класса описываются на C# в макете
обработки ИсходныйКод и компилируются «на лету» в обработчике ПриОткрытии формы.
Класс Helper отвечает за перенаправление .Net-события в функцию ОбработатьЗапрос
на форме 1С и формирование сообщение об ошибке.
Опустив не относящиеся к теме задачи по инициализации формы и вспомогательных классов,
следует остановиться на методе ОбработатьЗапрос, который отвечает за возврат результата
http-клиенту. Вызывается обработчик из класса Helper, в качестве параметра принимает
объект контекста, может возвратить сообщение об ошибке, которое будет возвращено
клиенту.
Объект контекста типа HttpListenerContext, передаваемый в метод ОбработатьЗапрос
содержит два важных свойства: Request и Response, отвечающие за информацию о запросе
и ответе соответственно.
Свойство Request позволяет получить параметры и содержимое, переданные клиентом
при запросе. Информация о запросе, помещенная в свойство Request содержит такие
основные свойства как:
AcceptTypes – MIME-типы, поддерживаемые клиентом
ContentEncoding – информация о кодировке при ответе
Headers – набор заголовков
HttpMethod – метод HTTP, определенный клиентом
InputStream – поток, содержащий данные тела, пришедшие от клиента
IsAuthenticated – булево значение, показывающее идентифицирован ли пользователь
IsLocal – значение, показывающее локальный ли запрос (через localhost)
QueryString – строка запроса из запроса
RawUrl – информация об URL без хоста и порта
UrlReferrer – URL ресурса-источник данного перехода
UserAgent – информация о агенте-браузере пользователя
Через свойство Response осуществляется возврат контента клиенту, передача информации
об ошибке или перенаправление. Сопровождаться все может выставлением необходимых
заголовков. Многие свойства ответа (Response) схожи со свойствами запроса. Значимые
среди них следующие:
ContentEncoding – информация о кодировке при ответе
Headers – набор заголовков
OutputStream – поток, в который будет записан ответ (например, текст Html, XML или
байтовый массив изображения)
RedirectLocation – свойство отвечает за HTTP-заголовок Location и позволяет перенаправить
вызов
StatusCode – код статсуса при возврате клиенту, например: 200 (OK), 404 (ресурс не
найден)
StatusDescription – описание статуса при возврате клиенту
Следующий код метода ОбработатьЗапрос преобразует сформированную строку РезультатHTML
с HTML-кодом в набор байт и записывает этот набор байт в выходной поток, который
будет возвращен клиенту. Ссылка на выходной поток получена через параметр метода.
ответСервера = context.Response;
массивБайт = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(РезультатHTML);
ответСервера.ContentLength64 = массивБайт.Length;
выходнойПоток = ответСервера.OutputStream;
выходнойПоток.Write(массивБайт, 0, массивБайт.Length);
выходнойПоток.Close();
Основой обработки является класс HttpServer, который создает объект HttpListener
и нужное число потоков для обработки. При вызове метода Start происходит запуск
всех потоков-обработчиков и отдельного потока для работы HttpListener. Благодаря
этому, можно продолжать работать с 1С во время работы http-сервера. При поступлении
запроса HttpListener помещает запрос в очередь, где каждый запрос последовательно
обрабатывает первый свободный поток. При обработке потока срабатывает цепочка вызовов:
событие HttpServer.ProcessRequest, обработчик события Helper. HttpServer_ProcessRequest,
1С-функция Форма. ОбработатьЗапрос. Код C# класса HttpServer при запуске 1C из макета
ИсходныйКод компилируется «на лету».
_listener.AuthenticationSchemes = authenticationScheme;
_listener.Prefixes.Add(String.Format(@"http://+:{0}/", port));
_listener.Start();
_listenerThread.Start();
for (int i = 0; i < _workers.Length; i++)
{
_workers[i] = new Thread(Worker);
_workers[i].Start();
}
Начало получения запросов происходит в методе HandleRequests до тех пор, пока обработка
не прекратится пользователем. При поступлении запроса, запрос передается в метод
ContextReady и работа процесса прослушивания продолжается.
private void HandleRequests()
{
while (_listener.IsListening)
{
var context = _listener.BeginGetContext(ContextReady, null);
if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
return;
}
}
private void Worker()
{
WaitHandle[] wait = new[] { _ready, _stop };
while (0 == WaitHandle.WaitAny(wait))
{
HttpListenerContext context;
lock (_queue)
{
if (_queue.Count > 0)
context = _queue.Dequeue();
else
{
_ready.Reset();
continue;
}
}
try { ProcessRequest(context); }
catch (Exception e) { Console.Error.WriteLine(e); }
}
}
Заключение
Многопоточный сервер, описанный в статье, по простоте настройки и функциональности
превосходит традиционные методы доступа извне к информационным базам 1С: Предприятие
8.х. Это простой по запуску метод, позволяющий одновременно обрабатывать несколько
запросов. При этом приложение 1С не блокируется, и пользователь может продолжать
работу после запуска сервера.
Основное преимущество предложенного подхода – полная подконтрольность программисту
процесса от получения запроса до формирования ответа. Например, на этапе получения
запроса может быть выполнен парсинг URL, получена информация о том, как себя идентифицировал
пользователь, а также полная информация о клиенте (поддерживаемые языки, записанные
куки, заголовки, метод доступа). Ответ же можно вернуть практически любой, начиная
от ошибки 404 Not found, заканчивая разными графическими форматами, форматами Word,
Excel и популярными форматами на основе XML (JSON, HTML, RSS).
Пример, приложенный к статье, спроектирован так, что его функциональность можно
легко расширить. Например, для организации кеша применить System.Web.Caching.Cache
класс из .Net framework. А при парсинге URL попробовать поработать с классом RouteCollection
из Asp.Net MVC. При создании RSS-ленты вам поможет класс System.ServiceModel.Syndication
.SyndicationFeed. А при Json-сериализации обратите внимание на класс System.Runtime.Serialization.Json.DataContractJsonSerializer.
Конкретно для предложенного подхода на данный момент не выявлено явных недостатков.
Есть вероятность того, что 1С: Предприятие в силу своих ограничений не сможет обеспечить
на своей стороне должного распараллеливания и значительного увеличения производительности.
Тем не менее опыты проведенные ранее для 1С: Предприятие 8.2 показали, что в схожем
применении 1С достигается увеличение производительности, и 1С работает при этом
стабильно.
Необходимо обратить внимание разработчиков еще на несколько моментов. Любая публикация
информации в Интернете связана с риском взлома вне зависимости от способа публикации.
Но в предложенном способе благодаря гибкости есть больше возможностей противостоять
угрозам извне. Например, в запросе теперь доступен IP-адрес клиента, который можно
блокировать по каким-то правилам (осуществляя поиск в черном списке локально или
в специализированных сервисах). Параметры запроса доступны в виде строк и их можно
анализировать на стороне 1С или .Net framework и блокировать опасное содержимое.
Кроме этого есть несколько специализированных .Net-библиотек, доступных для решения
данной проблемы, которые можно привлечь в 1С, например AntiXSS.
Второй момент заключается в том, что известные методы, в том числе и этот, предоставления
услуг со стороны 1С не предназначены для массовых обращений и будут всегда уступать
с этой точки зрения профессиональным серверам, например, IIS.
Примеры к статье:
HttpServer82.epf (11.50 kb)
TestHttpServer82.epf (8.30 kb)
Вопросы для собеседования
Servlets, JSP, JSTL
- Что такое «сервлет»?
- В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?
- Какова структура веб-проекта?
- Что такое «контейнер сервлетов»?
- Зачем нужны сервера приложений, если есть контейнеры сервлетов?
- Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?
- Что такое «дескриптор развертывания»?
- Какие действия необходимо проделать при создании сервлетов?
- В каком случае требуется переопределять метод
service()
? - Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?
- Почему необходимо переопределить только
init()
метод без аргументов? - Какие наиболее распространенные задачи выполняются в контейнере сервлетов?
- Что вы знаете о сервлетных фильтрах?
- Зачем в сервлетах используются различные listener?
- Когда стоит использовать фильтры сервлетов, а когда слушателей?
- Как реализовать запуск сервлета одновременно с запуском приложения?
- Как обработать в приложении исключения, выброшенные другим сервлетом?
- Что представляет собой
ServletConfig
? - Что представляет собой
ServletContext
? - В чем отличия
ServletContext
иServletConfig
? - Для чего нужен интерфейс
ServletResponse
? - Для чего нужен интерфейс
ServletRequest
? - Что такое
Request Dispatcher
? - Как из одного сервлета вызвать другой сервлет?
- Чем отличается
sendRedirect()
отforward()
? - Для чего используются атрибуты сервлетов и как происходит работа с ними?
- Каким образом можно допустить в сервлете deadlock?
- Как получить реальное расположение сервлета на сервере?
- Как получить информацию о сервере из сервлета?
- Как получить IP адрес клиента на сервере?
- Какие классы-обертки для сервлетов вы знаете?
- В чем отличия
GenericServlet
иHttpServlet
? - Почему
HttpServlet
класс объявлен как абстрактный? - Какие основные методы присутствуют в классе
HttpServlet
? - Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
- Какой метод HTTP не является неизменяемым?
- Какие есть методы отправки данных с клиента на сервер?
- В чем разница между методами
GET
иPOST
? - В чем разница между
PrintWriter
иServletOutputStream
? - Можно ли одновременно использовать в сервлете
PrintWriter
иServletOutputStream
? - Расскажите об интерфейсе
SingleThreadModel
. - Что означает URL encoding? Как это осуществить в Java?
- Какие различные методы управления сессией в сервлетах вы знаете?
- Что такое cookies?
- Какие методы для работы с cookies предусмотрены в сервлетах?
- Что такое URL Rewriting?
- Зачем нужны и чем отличаются методы
encodeURL()
иencodeRedirectURL()
? - Что такое «сессия»?
- Как уведомить объект в сессии, что сессия недействительна или закончилась?
- Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?
- Как мы можем обеспечить transport layer security для нашего веб приложения?
- Как организовать подключение к базе данных, обеспечить журналирование в сервлете?
- Какие основные особенности появились в спецификации Servlet 3?
- Какие способы аутентификации доступны сервлету?
- Что такое Java Server Pages (JSP)?
- Зачем нужен JSP?
- Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
- Расскажите об этапах (фазах) жизненного цикла JSP.
- Расскажите о методах жизненного цикла JSP.
- Какие методы жизненного цикла JSP могут быть переопределены?
- Как можно предотвратить прямой доступ к JSP странице из браузера?
- Какая разница между динамическим и статическим содержимым JSP?
- Как закомментировать код в JSP?
- Какие существуют основные типы тегов JSP?
- Что вы знаете о действиях JSP (Action tag и JSP Action Elements).
- Взаимодействие JSP — сервлет — JSP.
- Какие области видимости переменных существуют в JSP?
- Какие неявные, внутренние объекты и методы есть на JSP странице?
- Какие неявные объекты не доступны в обычной JSP странице?
- Что вы знаете о
PageContext
и какие преимущества его использования? - Как сконфигурировать параметры инициализации для JSP?
- Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?
- Можно ли определить класс внутри JSP страницы?
- Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?
- Какие типы EL операторов вы знаете?
- Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.
- Как отключить возможность использования EL в JSP?
- Как узнать тип HTTP метода используя JSP EL?
- Что такое JSTL (JSP Standard tag library)?
- Из каких групп тегов состоит библиотека JSTL?
- Какая разница между
<c:set>
и<jsp:useBean>
? - Чем отличается
<c:import>
от<jsp:include>
и директивы<%@include %>
? - Как можно расширить функциональность JSP?
- Что вы знаете о написании пользовательских JSP тегов?
- Приведите пример использования собственных тегов.
- Как сделать перенос строки в HTML средствами JSP?
- Почему не нужно конфигурировать стандартные JSP теги в
web.xml
? - Как можно обработать ошибки JSP страниц?
- Как происходит обработка ошибок с помощью JSTL?
- Как конфигурируется JSP в дескрипторе развертывания.
- Можно ли использовать Javascript на JSP странице?
- Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?
- Какая разница между
JSPWriter
и сервлетнымPrintWriter
? - Опишите общие практические принципы работы с JSP.
Что такое «сервлет»?
Сервлет является интерфейсом, реализация которого расширяет функциональные возможности сервера. Сервлет взаимодействует с клиентами посредством принципа запрос-ответ. Хотя сервлеты могут обслуживать любые запросы, они обычно используются для расширения веб-серверов.
Большинство необходимых для создания сервлетов классов и интерфейсов содержатся в пакетах javax.servlet
и javax.servlet.http
.
Основные методы сервлета:
public void init(ServletConfig config) throws ServletException
запускается сразу после загрузки сервлета в память;public ServletConfig getServletConfig()
возвращает ссылку на объект, который предоставляет доступ к информации о конфигурации сервлета;public String getServletInfo()
возвращает строку, содержащую информацию о сервлете, например: автор и версия сервлета;public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException
вызывается для обработки каждого запроса;public void destroy()
выполняется перед выгрузкой сервлета из памяти.
Текущая спецификация — Servlet 3.1 описана в JSR-340 и принята в 2013 году.
к оглавлению
В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?
- Сервлеты предоставляют лучшую производительность обработки запросов и более эффективное использование памяти за счет использования преимущество многопоточности (на каждый запрос создается новая нить, что быстрее выделения памяти под новый объект для каждого запроса, как это происходит в CGI).
- Сервлеты, как платформа и система являются независимыми. Таким образом веб-приложение, написанное с использованием сервлетов может быть запущена в любом контейнере сервлетов, реализующим этот стандарт и в любой операционной системе.
- Использование сервлетов повышает надежность программы, т.к. контейнер сервлетов самостоятельно заботится о жизненном цикле сервлетов (а значит и за утечками памяти), безопасности и сборщике мусора.
- Сервлеты относительно легки в изучении и поддержке, таким образом разработчику необходимо заботиться только о бизнес-логике приложения, а не внутренней реализации веб-технологий.
к оглавлению
Какова структура веб-проекта?
src/main/java
Исходники приложения/библиотеки
src/main/resources
Ресурсные файлы приложения/библиотеки
src/main/filters
Файлы сервлетных фильтров
src/main/webapp
Исходники веб-приложения
src/test/java
Исходники тестов
src/test/resources
Ресурсные файлы тестов
src/test/filters
Тесты сервлетных фильтров
src/it
Интеграционные тесты
src/assembly
Описание сборки
src/site
Сайт
LICENSE.txt
Лицензия проекта
NOTICE.txt
Замечания и определения библиотек зависимостей.
README.txt
Описание проекта
к оглавлению
Что такое «контейнер сервлетов»?
Контейнер сервлетов — программа, представляющая собой сервер, который занимается системной поддержкой сервлетов и обеспечивает их жизненный цикл в соответствии с правилами, определёнными в спецификациях. Может работать как полноценный самостоятельный веб-сервер, быть поставщиком страниц для другого веб-сервера, или интегрироваться в Java EE сервер приложений.
Контейнер сервлетов обеспечивает обмен данными между сервлетом и клиентами, берёт на себя выполнение таких функций, как создание программной среды для функционирующего сервлета, идентификацию и авторизацию клиентов, организацию сессии для каждого из них.
Наиболее известные реализации контейнеров сервлетов:
- Apache Tomcat
- Jetty
- JBoss
- WildFly
- GlassFish
- IBM WebSphere
- Oracle Weblogic
к оглавлению
Зачем нужны сервера приложений, если есть контейнеры сервлетов?
- Пулы соединений с БД
- Возможность периодического тестирования доступности СУБД и обновления соединения в случае восстановления после сбоев
- Замена прав доступа при подключении
- Балансировка нагрузки между несколькими СУБД, определение доступность или недоступность того или иного узла
- Защита пула соединений от некорректного кода в приложении, которое по недосмотру не возвращает соединения, просто отбирая его после какого-то таймаута.
- JMS
- Доступность сервера очередей сообщений «из-коробки».
- Возможность кластеризации очередей, т.е. доступность построения распределенных очередей, расположенных сразу на нескольких серверах, что существенно увеличивает масштабируемость и доступность приложения
- Возможность миграции очередей — в случае падения одного из серверов, его очереди автоматически перемещаются на другой, сохраняя необработанные сообщения.
- В некоторых серверах приложений поддерживается Unit-of-Order — гарантированный порядок обработки сообщений, удовлетворяющих некоторым критериям.
- JTA Встроенная поддержка распределенных транзакций для обеспечения согласованности данных в разные СУБД или очереди.
- Безопасность
- Наличие множества провайдеров безопасности и аутентификации:
- во встроенном или внешнем LDAP-сервере
- в базе данных
- в различных Internet-directory (специализированных приложениях для управления правами доступа)
- Доступность Single-Sign-On (возможности разделения пользовательской сессии между приложениями) посредством Security Assertion Markup Language (SAML) 1/2 или Simple and Protected Negotiate (SPNEGO) и Kerberos: один из серверов выступает в роли базы для хранения пользователей, все другие сервера при аутентификации пользователя обращаются к этой базе.
- Возможность авторизации посредством протокола eXtensible Access Control Markup Language (XACML), позволяющего описывать довольно сложные политики (например, приложение доступно пользователю только в рабочее время).
- Кластеризация всего вышеперечисленного
- Наличие множества провайдеров безопасности и аутентификации:
- Масштабируемость и высокая доступность Для контейнера сервлетов обычно так же возможно настроить кластеризацию, но она будет довольно примитивной, так как в случае его использования имеются следующие ограничения:
- Сложность передачи пользовательской сессии из одного центра обработки данных (ЦоД) в другой через Интернет
- Отсутствие возможности эффективно настроить репликации сессий на большом (состоящем из 40-50 экземпляров серверов) кластере
- Невозможность обеспечения миграции экземпляров приложения на другой сервер
- Недоступность механизмов автоматического мониторинга и реакции на ошибки
- Управляемость
- Присутствие единого центра управления, т.н. AdminServer и аналога NodeManager’а, обеспечивающего
- Возможность одновременного запуска нескольких экземпляров сервера
- Просмотр состояния запущенных экземпляров сервера, обработчиков той или иной очереди, на том или ином сервере, количества соединений с той или иной БД
- Присутствие единого центра управления, т.н. AdminServer и аналога NodeManager’а, обеспечивающего
- Административный канал и развертывание в промышленном режиме Некоторые сервера приложений позволяют включить так называемый «административный канал» — отдельный порт, запросы по которому имеют приоритет.
- Просмотр состояния (выполняющихся транзакций, потоков, очередей) в случае недоступности («зависания») сервера
- Обновление приложений «на-лету», без простоя:
- добавление на сервер новой версии приложения в «закрытом» режиме, пока пользователи продолжают работать со предыдущей
- тестирование корректности развертывания новой версии
- «скрытый» перевод на использование новой версии всех пользователей
к оглавлению
Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?
Контейнер сервлетов управляет четырьмя фазами жизненного цикла сервлета:
- Загрузка класса сервлета — когда контейнер получает запрос для сервлета, то происходит загрузка класса сервлета в память и вызов его конструктора без параметров.
- Инициализация класса сервлета — после того как класс загружен контейнер инициализирует объект
ServletConfig
для этого сервлета и внедряет его черезinit()
метод. Это и есть место где сервлет класс преобразуется из обычного класса в сервлет. - Обработка запросов — после инициализации сервлет готов к обработке запросов. Для каждого запроса клиента сервлет контейнер порождает новый поток и вызывает метод
service()
путем передачи ссылки на объекты ответа и запроса. - Удаление — когда контейнер останавливается или останавливается приложение, то контейнер сервлетов уничтожает классы сервлетов путем вызова
destroy()
метода.
Таким образом, сервлет создаётся при первом обращении к нему и живёт на протяжении всего времени работы приложения (в отличии от объектов классов, которые уничтожаются сборщиком мусора после того, как они уже не используются) и весь жизненный цикл сервлета можно описать как последовательность вызова методов:
public void init(ServletConfig config)
– используется контейнером для инициализации сервлета. Вызывается один раз за время жизни сервлета.public void service(ServletRequest request, ServletResponse response)
– вызывается для каждого запроса. Метод не может быть вызван раньше выполненияinit()
метода.public void destroy()
– вызывается для уничтожения сервлета (один раз за время жизни сервлета).
к оглавлению
Что такое «дескриптор развертывания»?
Дескриптор развертывания — это конфигурационный файл артефакта, который будет развернут в контейнере сервлетов. В спецификации Java Platform, Enterprise Edition дескриптор развертывания описывает то, как компонент, модуль или приложение (такое, как веб-приложение или приложение предприятия) должно быть развернуто.
Этот конфигурационный файл указывает параметры развертывания для модуля или приложения с определенными настройками, параметры безопасности и описывает конкретные требования к конфигурации. Для синтаксиса файлов дескриптора развертывания используется язык XML.
<?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <display-name>Display name.</display-name> <description>Description text.</description> <servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>configuration</param-name> <param-value>default</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ExampleServlet</servlet-name> <url-pattern>/example</url-pattern> </servlet-mapping> <servlet> <servlet-name>ExampleJSP</servlet-name> <jsp-file>/sample/Example.jsp</jsp-file> </servlet> <context-param> <param-name>myParam</param-name> <param-value>the value</param-value> </context-param> </web-app>
Для веб-приложений дескриптор развертывания должен называться web.xml
и находиться в директории WEB-INF
, в корне веб-приложения. Этот файл является стандартным дескриптором развертывания, определенным в спецификации. Также есть и другие типы дескрипторов, такие, как файл дескриптора развертывания sun-web.xml
, содержащий специфичные для Sun GlassFish Enterprise Server данные для развертывания именно для этого сервера приложений или файл application.xml
в директории META-INF
для приложений J2EE.
к оглавлению
Какие действия необходимо проделать при создании сервлетов?
Чтобы создать сервлет ExampleServlet
, необходимо описать его в дескрипторе развёртывания:
<servlet-mapping> <servlet-name>ExampleServlet</servlet-name> <url-pattern>/example</url-pattern> </servlet-mapping> <servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>default</param-value> </init-param> </servlet>
Затем создать класс xyz.company.ExampleServlet
путём наследования от HttpServlet
и реализовать логику его работы в методе service()
или методах doGet()
/doPost()
.
к оглавлению
В каком случае требуется переопределять метод service()
?
Метод service()
переопределяется, когда необходимо, чтобы сервлет обрабатывал все запросы (и GET
, и POST
) в одном методе.
Когда контейнер сервлетов получает запрос клиента, то происходит вызов метода service()
, который в зависимости от поступившего запроса вызывает или метод doGet()
или метод doPost()
.
к оглавлению
Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?
Большого смысла определять для сервлета конструктор нет, т.к. инициализировать данные лучше не в конструкторе, а переопределив метод init()
, в котором имеется возможность доступа к параметрам инициализации сервлета через использование объекта ServletConfig
.
к оглавлению
Почему необходимо переопределить только init()
метод без аргументов?
Метод init()
переопределяется, если необходимо инициализировать какие-то данные до того как сервлет начнет обрабатывать запросы.
При переопределении метода init(ServletConfig config)
, первым должен быть вызван метод super(config)
, который обеспечит вызов метода init(ServletConfig config)
суперкласса. GenericServlet
предоставляет другой метод init()
без параметров, который будет вызываться в конце метода init(ServletConfig config)
.
Необходимо использовать переопределенный метод init()
без параметров для инициализации данных во избежание каких-либо проблем, например ошибку, когда вызов super()
не указан в переопределенном init(ServletConfig config)
.
к оглавлению
Какие наиболее распространенные задачи выполняются в контейнере сервлетов?
- Поддержка обмена данными. Контейнер сервлетов предоставляет легкий способ обмена данными между веб клиентом (браузером) и сервлетом. Благодаря контейнеру нет необходимости создавать слушателя сокета на сервере для отслеживания запросов от клиента, а также разбирать запрос и генерировать ответ. Все эти важные и комплексные задачи решаются с помощью контейнера и разработчик может сосредоточиться на бизнес логике приложения.
- Управление жизненным циклом сервлетов и ресурсов. Начиная от загрузки сервлета в память, инициализации, внедрения методов и заканчивая уничтожением сервлета. Контейнер так же предоставляет дополнительные утилиты, например JNDI, для управления пулом ресурсов.
- Поддержка многопоточности. Контейнер самостоятельно создает новую нить для каждого запроса и предоставляет ей запрос и ответ для обработки. Таким образом сервлет не инициализируется заново для каждого запроса и тем самым сохраняет память и уменьшает время до обработки запроса.
- Поддержка JSP. JSP классы не похожи на стандартные классы джавы, но контейнер сервлетов преобразует каждую JSP в сервлет и далее управляется контейнером как обычным сервлетом.
- Различные задачи. Контейнер сервлетов управляет пулом ресурсов, памятью приложения, сборщиком мусора. Предоставляются возможности настройки безопасности и многое другое.
к оглавлению
Что вы знаете о сервлетных фильтрах?
Сервлетный фильтр — это Java-код, пригодный для повторного использования и позволяющий преобразовать содержание HTTP-запросов, HTTP-ответов и информацию, содержащуюся в заголовках HTML. Сервлетный фильтр занимается предварительной обработкой запроса, прежде чем тот попадает в сервлет, и/или последующей обработкой ответа, исходящего из сервлета.
Сервлетные фильтры могут:
- перехватывать инициацию сервлета прежде, чем сервлет будет инициирован;
- определить содержание запроса прежде, чем сервлет будет инициирован;
- модифицировать заголовки и данные запроса, в которые упаковывается поступающий запрос;
- модифицировать заголовки и данные ответа, в которые упаковывается получаемый ответ;
- перехватывать инициацию сервлета после обращения к сервлету.
Сервлетный фильтр может быть конфигурирован так, что он будет работать с одним сервлетом или группой сервлетов. Основой для формирования фильтров служит интерфейс javax.servlet.Filter
, который реализует три метода:
void init(FilterConfig config) throws ServletException
;void destroy()
;void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
;
Метод init()
вызывается прежде, чем фильтр начинает работать,и настраивает конфигурационный объект фильтра. Метод doFilter()
выполняет непосредственно работу фильтра. Таким образом, сервер вызывает init()
один раз, чтобы запустить фильтр в работу, а затем вызывает doFilter()
столько раз, сколько запросов будет сделано непосредственно к данному фильтру. После того, как фильтр заканчивает свою работу, вызывается метод destroy()
.
Интерфейс FilterConfig
содержит метод для получения имени фильтра, его параметров инициации и контекста активного в данный момент сервлета. С помощью своего метода doFilter()
каждый фильтр получает текущий запрос request
и ответ response
, а также FilterChain
, содержащий список фильтров, предназначенных для обработки. В doFilter()
фильтр может делать с запросом и ответом всё, что ему захочется — собирать данные или упаковывать объекты для придания им нового поведения. Затем фильтр вызывает chain.doFilter()
, чтобы передать управление следующему фильтру. После возвращения этого вызова фильтр может по окончании работы своего метода doFilter()
выполнить дополнительную работу над полученным ответом. К примеру, сохранить регистрационную информацию об этом ответе.
После того, как класс-фильтр откомпилирован, его необходимо установить в контейнер и «приписать» (map) к одному или нескольким сервлетам. Объявление и подключение фильтра отмечается в дескрипторе развёртывания web.xml
внутри элементов <filter>
и <filter-mapping>
. Для подключение фильтра к сервлету необходимо использовать вложенные элементы <filter-name>
и <servlet-name>
.
Объявление класс-фильтра
FilterConnect
с именемFilterName
:
<filter> <filter-name>FilterName</filter-name> <filter-class>FilterConnect</filter-class> <init-param> <!--- фильтр имеет параметр инициализации `active`, которому присваивается значение `true`. --> <param-name>active</param-name> <param-value>true</param-true> </init-param> </filter>
Подключение фильтра
FilterName
к сервлетуServletName
:
<filter-mapping> <filter-name>FilterName</filter-name> <servlet-name>ServletName</servlet-name> </filter-mapping>
Для связи фильтра со страницами HTML или группой сервлетов необходимо использовать тег <url-pattern>
:
Подключение фильтра
FilterName
ко всем вызовам .html страниц
<filter-mapping> <filter-name>FilterName</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping>
Порядок, в котором контейнер строит цепочку фильтров для запроса определяется следующими правилами:
- цепочка, определяемая
<url-pattern>
, выстраивается в том порядке, в котором встречаются соответствующие описания фильтров вweb.xml
; - последовательность сервлетов, определенных с помощью
<servlet-name>
, также выполняется в той последовательности, в какой эти элементы встречаются в дескрипторе развёртыванияweb.xml
.
к оглавлению
Зачем в сервлетах используются различные listener?
Listener (слушатель) работает как триггер, выполняя определённые действия при наступлении какого-либо события в жизненном цикле сервлета.
Слушатели, разделённые по области видимости (scope):
- Request:
ServletRequestListener
используется для того, чтобы поймать момент создания и уничтожения запроса;ServletRequestAttributeListener
используется для прослушивания событий, происходящих с атрибутами запроса.
- Context:
ServletContextListener
позволяет поймать момент, когда контекст инициализируется либо уничтожается;ServletContextAttributeListener
используется для прослушивании событий, происходящих с атрибутами в контексте.
- Session:
HttpSessionListener
позволяет поймать момент создания и уничтожения сессии;HttpSessionAttributeListener
используется при прослушивании событий происходящих с атрибутами в сессии;HttpSessionActivationListener
используется в случае, если происходит миграция сессии между различными JVM в распределённых приложениях;HttpSessionBindingListener
так же используется для прослушивания событий, происходящих с атрибутами в сессии. Разница междуHttpSessionAttributeListener
иHttpSessionBindingListener
слушателями: первый декларируется вweb.xml
; экземпляр класса создается контейнером автоматически в единственном числе и применяется ко всем сессиям; второй: экземпляр класса должен быть создан и закреплён за определённой сессией «вручную», количество экземпляров также регулируется самостоятельно.
Подключение слушателей:
<web-app> ... <listener> <listener-class>xyz.company.ExampleListener</listener-class> </listener> ... </web-app>
HttpSessionBindingListener
подключается в качестве атрибута непосредственно в сессию, т.е., чтобы его подключить необходимо:
- создать экземпляр класса, реализующего этот интерфейс;
- положить созданный экземпляр в сессию при помощи
setAttribute(String, Object)
.
к оглавлению
Когда стоит использовать фильтры сервлетов, а когда слушателей?
Следует использовать фильтры, если необходимо обрабатывать входящие или исходящие данные (например: для аутентификации, преобразования формата, компрессии, шифрования и т.д.), в случае, когда необходимо реагировать на события — лучше применять слушателей.
к оглавлению
Как реализовать запуск сервлета одновременно с запуском приложения?
Контейнер сервлетов обычно загружает сервлет по первому запросу клиента.
Если необходимо загрузить сервлет прямо на старте приложения (например если загрузка сервлета происходит длительное время) следует использовать элемент <load-on-startup>
в дескрипторе или аннотацию @loadOnStartup
в коде сервлета, что будет указывать на необходимость загрузки сервлета при запуске.
Если целочисленное значение этого параметра отрицательно, то сервлет будет загружен при запросе клиента. В противном случае — загрузится на старте приложения, при этом, чем число меньше, тем раньше в очереди на загрузку он окажется.
<servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
к оглавлению
Как обработать в приложении исключения, выброшенные другим сервлетом?
Когда приложение выбрасывет исключение контейнер сервлетов обрабатывает его и создаёт ответ в формате HTML. Это аналогично тому, что происходит при кодах ошибок вроде 404, 403 и т.д.
В дополнении к этому существует возможность написания собственных сервлетов для обработки исключений и ошибок с указанием их в дескрипторе развертывания:
<error-page> <error-code>404</error-code> <location>/AppExceptionHandler</location> </error-page> <error-page> <exception-type>javax.servlet.ServletException</exception-type> <location>/AppExceptionHandler</location> </error-page>
Основная задача таких сервлетов — обработать ошибку/исключение и сформировать понятный ответ пользователю. Например, предоставить ссылку на главную страницу или же описание ошибки.
к оглавлению
Что представляет собой ServletConfig
?
Интерфейс javax.servlet.ServletConfig
используется для передачи сервлету конфигурационной информации. Каждый сервлет имеет свой собственный экземпляр объекта ServletConfig
, создаваемый контейнером сервлетов.
Для установки параметров конфигурации используются параметры init-param
в web.xml
:
<servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <init-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </init-param> </servlet>
или аннотации @WebInitParam
:
@WebServlet( urlPatterns = "/example", initParams = { @WebInitParam(name = "exampleParameter", value = "parameterValue") } ) public class ExampleServlet extends HttpServlet { //... }
Для получения ServletConfig
сервлета используется метод getServletConfig()
.
к оглавлению
Что представляет собой ServletContext
?
Уникальный (в рамках веб-приложения) объект ServletContext
реализует интерфейс javax.servlet.ServletContext
и предоставляет сервлетам доступ к параметрам этого веб-приложения. Для предоставления доступа используется элемент <context-param>
в web.xml
:
<web-app> ... <context-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </context-param> ... </web-app>
Объект ServletContext
можно получить с помощью метода getServletContext()
у интерфейса ServletConfig
. Контейнеры сервлетов так же могут предоставлять контекстные объекты, уникальные для группы сервлетов. Каждая из групп будет связана со своим набором URL-путей хоста. В спецификации Servlet 3 ServletContext
был расширен и теперь предоставляет возможности программного добавления слушателей и фильтров в приложение. Так же у этого интерфейса имеется множество полезных методов таких как getServerInfo()
, getMimeType()
, getResourceAsStream()
и т.д.
к оглавлению
В чем отличия ServletContext
и ServletConfig
?
ServletConfig
уникален для сервлета, аServletContext
— для приложения;ServletConfig
используется для предоставления параметров инициализации конкретному сервлету, аServletContext
для предоставления параметров инициализации для всех сервлетов приложения;- для
ServletConfig
возможности модифицировать атрибуты отсутствуют, атрибуты в объектеServletContext
можно изменять.
к оглавлению
Для чего нужен интерфейс ServletResponse
?
Интерфейс ServletResponse
используется для отправки данных клиенту. Все методы данного инструмента служат именно этой цели:
String getCharacterEncoding()
— возвращает MIME тип кодировки (к примеру — UTF8), в которой будет выдаваться информация;void setLocale(Locale locale)
/Locale getLocale()
— указывают на язык используемый в документе;ServletOutputStream getOutputStream()
/PrintWriter getWriter()
— возвращают потоки вывода данных;void setContentLength(int len)
— устанавливает значение поля HTTP заголовка Content-Length;void setContentType(String type)
— устанавливает значение поля HTTP заголовка Content-Type.void reset()
— позволяет сбросить HTTP заголовок к значениям по-умолчанию, если он ещё не был отправлен- и др.
к оглавлению
Для чего нужен интерфейс ServletRequest
?
Интерфейс ServletRequest
используется для получения параметров соединения, запроса, а также заголовков, входящего потока данных и т.д.
к оглавлению
Что такое Request Dispatcher
?
Интерфейс RequestDispatcher
используется для передачи запроса другому ресурсу, при этом существует возможность добавления данных, полученных из этого ресурса к собственному ответу сервлета. Так же этот интерфейс используется для внутренней коммуникации между сервлетами в одном контексте.
В интерфейсе объявлено два метода:
void forward(ServletRequest var1, ServletResponse var2)
— передает запрос из сервлета к другому ресурсу (сервлету, JSP или HTML файлу) на сервере.void include(ServletRequest var1, ServletResponse var2)
— включает контент ресурса (сервлет, JSP или HTML страница) в ответ.
Доступ к интерфейсу можно получить с помощью метода интерфейса ServletContext
— RequestDispatcher getRequestDispatcher(String path)
, где путь начинающийся с /
, интерпретируется относительно текущего корневого пути контекста.
к оглавлению
Как из одного сервлета вызвать другой сервлет?
Для вызова сервлета из того же приложения необходимо использовать механизм внутренней коммуникации сервлетов (inter-servlet communication mechanisms) через вызовы методов RequestDispatcher
:
forward()
— передаёт выполнение запроса в другой сервлет;include()
— предоставляет возможность включить результат работы другого сервлета в возвращаемый ответ.
Если необходимо вызывать сервлет принадлежащий другому приложению, то использовать RequestDispatcher
уже не получится, т.к. он определен только для текущего приложения. Для подобных целей необходимо использовать метод ServletResponse
— sendRedirect()
которому предоставляется полный URL другого сервлета. Для передачи данных между сервлетами можно использовать cookies
.
к оглавлению
Чем отличается sendRedirect()
от forward()
?
forward()
:
- Выполняется на стороне сервера;
- Запрос перенаправляется на другой ресурс в пределах того же сервера;
- Не зависит от протокола клиентского запроса, так как обеспечивается контейнером сервлетов;
- Нельзя применять для внедрения сервлета в другой контекст;
- Клиент не знает о фактически обрабатываемом ресурсе и URL в строке остается прежним;
- Выполняется быстрее метода
sendRedirect()
; - Определён в интерфейсе
RequestDispatcher
.
sendRedirect()
:
- Выполняется на стороне клиента;
- Клиенту возвращается ответ
302 (redirect)
и запрос перенаправляется на другой сервер; - Может использоваться только с клиентами HTTP;
- Разрешается применять для внедрения сервлета в другой контекст;
- URL адрес изменяется на адрес нового ресурса;
- Медленнее
forward()
т.к. требует создания нового запроса; - Определён в интерфейсе
HttpServletResponse
.
к оглавлению
Для чего используются атрибуты сервлетов и как происходит работа с ними?
Атрибуты сервлетов используются для внутренней коммуникации сервлетов.
В веб-приложении существует возможность работы с атрибутами используя методы setAttribute()
, getAttribute()
, removeAttribute()
, getAttributeNames()
, которые предоставлены интерфейсами ServletRequest
, HttpSession
и ServletContext
(для областей видимости request, session, context соответственно).
к оглавлению
Каким образом можно допустить в сервлете deadlock?
Можно получить блокировку, например, допустив циклические вызовы метода doPost()
в методе doGet()
и метода doGet()
в методе doPost()
.
к оглавлению
Как получить реальное расположение сервлета на сервере?
Реальный путь к расположению сервлета на сервере можно получить из объекта ServletContext
:
getServletContext().getRealPath(request.getServletPath())
.
к оглавлению
Как получить информацию о сервере из сервлета?
Информацию о сервере можно получить из объекта ServletContext
:
getServletContext().getServerInfo()
.
к оглавлению
Как получить IP адрес клиента на сервере?
IP адрес клиента можно получить вызвав request.getRemoteAddr()
.
к оглавлению
Какие классы-обертки для сервлетов вы знаете?
Собственные обработчики ServletRequest
и ServletResponse
можно реализовать, добавив новые или переопределив существующие методы у классов-обёрток ServletRequestWrapper
(HttpServletRequestWrapper
) и ServletResponseWrapper
(HttpServletRequestWrapper
).
к оглавлению
В чем отличия GenericServlet
и HttpServlet
?
Абстрактный класс GenericServlet
— независимая от используемого протокола реализация интерфейса Servlet
, а абстрактный класс HttpServlet
в свою очередь расширяет GenericServlet
для протокола HTTP..
к оглавлению
Почему HttpServlet
класс объявлен как абстрактный?
Класс HTTPServlet
предоставляет лишь общую реализацию сервлета для HTTP протокола. Реализация ключевых методов doGet()
и doPost()
, содержащих основную бизнес-логику перекладывается на разработчика и по умолчанию возвращает HTTP 405 Method Not Implemented error
.
к оглавлению
Какие основные методы присутствуют в классе HttpServlet
?
doGet()
— для обработки HTTP запросовGET
;doPost()
— для обработки HTTP запросовPOST
;doPut()
— для обработки HTTP запросовPUT
;doDelete()
— для обработки HTTP запросовDELETE
;doHead()
— для обработки HTTP запросовHEAD
;doOptions()
— для обработки HTTP запросовOPTIONS
;doTrace()
— для обработки HTTP запросовTRACE
.
к оглавлению
Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
Методы init()
и destroy()
вызываются один раз за жизненный цикл сервлета — поэтому по поводу них беспокоиться не стоит.
Методы doGet()
, doPost()
, service()
вызываются на каждый запрос клиента и т.к. сервлеты используют многопоточность, то здесь задумываться о потокобезопасной работе обязательно. При этом правила использования многопоточности остаются теми же: локальные переменные этих методов будут созданы отдельно для каждого потока, а при использовании глобальных разделяемых ресурсов необходимо использовать синхронизацию или другие приёмы многопоточного программирования.
к оглавлению
Какой метод HTTP не является неизменяемым?
HTTP метод называется неизменяемым, если он на один и тот же запрос всегда возвращает одинаковый результат. HTTP методы GET
, PUT
, DELETE
, HEAD
и OPTIONS
являются неизменяемыми, поэтому необходимо реализовывать приложение так, чтобы эти методы возвращали одинаковый результат постоянно. К изменяемым методам относится метод POST
, который и используется для реализации чего-либо, что изменяется при каждом запросе.
К примеру, для доступа к статической HTML странице используется метод GET
, т.к. он всегда возвращает одинаковый результат. При необходимости сохранять какую-либо информацию, например в базе данных, нужно использовать POST
метод.
к оглавлению
Какие есть методы отправки данных с клиента на сервер?
GET
— используется для запроса содержимого указанного ресурса, изображения или гипертекстового документа. Вместе с запросом могут передаваться дополнительные параметры как часть URI, значения могут выбираться из полей формы или передаваться непосредственно через URL. При этом запросы кэшируются и имеют ограничения на размер. Этот метод является основным методом взаимодействия браузера клиента и веб-сервера.POST
— используется для передачи пользовательских данных в содержимом HTTP-запроса на сервер. Пользовательские данные упакованы в тело запроса согласно полю заголовка Content-Type и/или включены в URI запроса. При использовании метода POST под URI подразумевается ресурс, который будет обрабатывать запрос.
к оглавлению
В чем разница между методами GET
и POST
?
GET
передает данные серверу используя URL, тогда какPOST
передает данные, используя тело HTTP запроса. Длина URL ограничена 1024 символами, это и будет верхним ограничением для данных, которые можно отослать черезGET
.POST
может отправлять гораздо большие объемы данных. Лимит устанавливается web-server и составляет обычно около 2 Mb.- Передача данных методом
POST
более безопасна, чем методомGET
, так как секретные данные (например пароль) не отображаются напрямую в web-клиенте пользователя, в отличии от URL, который виден почти всегда. Иногда это преимущество превращается в недостаток — вы не сможете послать данные за кого-то другого. GET
метод является неизменяемым, тогда какPOST
— изменяемый.
к оглавлению
В чем разница между PrintWriter
и ServletOutputStream
?
PrintWriter
— класс для работы с символьным потоком, экземпляр которого можно получить через метод ServletResponse
getWriter()
;
ServletOutputStream
— класс для работы байтовым потоком. Для получения его экземпляра используется метод ServletResponse
getOutputStream()
.
к оглавлению
Можно ли одновременно использовать в сервлете PrintWriter
и ServletOutputStream
?
Так сделать не получится, т.к. при попытке одновременного вызова getWriter()
и getOutputStream()
будет выброшено исключение java.lang.IllegalStateException
с сообщением, что уже был вызван другой метод.
к оглавлению
Расскажите об интерфейсе SingleThreadModel
.
Интерфейс SingleThreadModel
является маркерным — в нем не объявлен ни один метод, однако, если сервлет реализует этот интерфейс, то метод service()
этого сервлета гарантированно не будет одновременно выполняться в двух потоках. Контейнер сервлетов либо синхронизирует обращения к единственному экземпляру, либо обеспечивает поддержку пула экземпляров и перенаправление запроса свободному сервлету.
Другими словами, контейнер гарантирует отсутствие конфликтов при одновременном обращении к переменным или методам экземпляра сервлета. Однако существуют также и другие разделяемые ресурсы, которые даже при использовании этого интерфейса, остаются всё так же доступны обработчикам запросов в других потоках. Т.о. пользы от использования этого интерфейса немного и в спецификации Servlet 2.4 он был объявлен deprecated
.
к оглавлению
Что означает URL encoding? Как это осуществить в Java?
URL Encoding — процесс преобразования данных в форму CGI (Common Gateway Interface), не содержащую пробелов и нестандартных символов, которые заменяются в процессе кодирования на специальные escape-символы. В Java для кодирования строки используется метод java.net.URLEncoder.encode(String str, String unicode)
. Обратная операция декодирования возможна через использование метода java.net.URLDecoder.decode(String str, String unicode)
.
Hello мир!
преобразовывается вHello%20%D0%BC%D0%B8%D1%80!
.
к оглавлению
Какие различные методы управления сессией в сервлетах вы знаете?
При посещении клиентом Web-ресурса и выполнении вариантов запросов, контекстная информация о клиенте не хранится. В протоколе HTTP нет возможностей для сохранения и изменения информации о предыдущих посещениях клиента. Сеанс (сессия) – соединение между клиентом и сервером, устанавливаемое на определенное время, за которое клиент может отправить на сервер сколько угодно запросов. Сеанс устанавливается непосредственно между клиентом и Web-сервером. Каждый клиент устанавливает с сервером свой собственный сеанс. Сеансы используются для обеспечения хранения данных во время нескольких запросов Web-страницы или на обработку информации, введенной в пользовательскую форму в результате нескольких HTTP-соединений (например, клиент совершает несколько покупок в интернет-магазине; студент отвечает на несколько тестов в системе дистанционного обучения).
Существует несколько способов обеспечения уникального идентификатора сессии:
- User Authentication – Предоставление учетных данных самим пользователем в момент аутентификации. Переданная таким образом информация в дальнейшем используется для поддержания сеанса. Это метод не будет работать, если пользователь вошёл в систему одновременно из нескольких мест.
- HTML Hidden Field – Присвоение уникального значения скрытому полю HTML страницы, в момент когда пользователь начинает сеанс. Этот метод не может быть использован со ссылками, потому что нуждается в подтверждении формы со скрытым полем каждый раз во время формирования запроса. Кроме того, это не безопасно, т.к. существует возможность простой подмены такого идентификатора.
- URL Rewriting – Добавление идентификатора сеанса как параметра URL. Достаточно утомительная операция, потому что требует постоянного отслеживания этого идентификатора при каждом запросе или ответе.
- Cookies – Использование небольших фрагментов данных, отправленных web-сервером и хранимых на устройстве пользователя. Данный метод не будет работать, если клиент отключает использование cookies.
- Session Management API – Использование специального API для отслеживания сеанса, построенный на основе и на методах, описанных выше и который решает частные проблемы перечисленных способов:
- Чаще всего недостаточно просто отслеживать сессию, необходимо ещё и сохранять какие-либо дополнительные данные о ней, которые могут потребоваться при обработке последующих запросов. Осуществление такого поведения требует много дополнительных усилий.
- Все вышеперечисленные методы не являются универсальными: для каждого из них можно подобрать конкретный сценарий, при котором они не будут работать.
к оглавлению
Что такое cookies?
Сookies («куки») — небольшой фрагмент данных, отправленный web-сервером и хранимый на устройстве пользователя. Всякий раз при попытке открыть страницу сайта, web-клиент пересылает соответствующие этому сайту cookies web-серверу в составе HTTP-запроса. Применяется для сохранения данных на стороне пользователя и на практике обычно используется для:
- аутентификации пользователя;
- хранения персональных предпочтений и настроек пользователя;
- отслеживания состояния сеанса доступа пользователя;
- ведения разнообразной статистики.
к оглавлению
Какие методы для работы с cookies предусмотрены в сервлетах?
Servlet API предоставляет поддержку cookies через класс javax.servlet.http.Cookie
:
- Для получения массива cookies из запроса необходимо воспользоваться методом
HttpServletRequest.getCookies()
. Методов для добавления cookies вHttpServletRequest
не предусмотрено. - Для добавления cookie в ответ используется
HttpServletResponse.addCookie(Cookie c)
. Метода получения cookies вHttpServletResponse
отсутствует.
к оглавлению
Что такое URL Rewriting?
URL Rewriting — специальная перезапись (перекодирование) оригинального URL. Данный механизм может использоваться для управления сессией в сервлетах, когда cookies отключены.
к оглавлению
Зачем нужны и чем отличаются методы encodeURL()
и encodeRedirectURL()
?
HttpServletResponse.encodeURL()
предоставляет способ преобразования URL в HTML гиперссылку с преобразованием спецсимволов и пробелов, а так же добавления session id к URL. Такое поведение аналогично java.net.URLEncoder.encode()
, но с добавлением дополнительного параметра jsessionid
в конец URL.
Метод HttpServletResponse.encodeRedirectURL()
преобразует URL для последующего использования в методе sendRedirect()
.
Таким образом для HTML гиперссылок при URL rewriting необходимо использовать encodeURL()
, а для URL при перенаправлении — encodeRedirectUrl()
.
к оглавлению
Что такое «сессия»?
Сессия — это сеанс связи между клиентом и сервером, устанавливаемый на определенное время. Сеанс устанавливается непосредственно между клиентом и веб-сервером в момент получения первого запроса к веб-приложению. Каждый клиент устанавливает с сервером свой собственный сеанс, который сохраняется до окончания работы с приложением.
к оглавлению
Как уведомить объект в сессии, что сессия недействительна или закончилась?
Чтобы быть уверенным в том, что объект будет оповещён о прекращении сессии, нужно реализовать интерфейс javax.servlet.http.HttpSessionBindingListener
. Два метода этого интерфейса: valueBound()
и valueUnbound()
используются при добавлении объекта в качестве атрибута к сессии и при уничтожении сессии соответственно.
к оглавлению
Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?
Сервлет фильтры используются для перехвата всех запросов между контейнером сервлетов и сервлетом. Поэтому логично использовать соответствующий фильтр для проверки необходимой информации (например валидности сессии) в запросе.
к оглавлению
Как мы можем обеспечить transport layer security для нашего веб приложения?
Для обеспечения transport layer security необходимо настроить поддержку SSL сервлет контейнера. Как это сделать зависит от конкретной реализации сервлет-контейнера.
к оглавлению
Как организовать подключение к базе данных, обеспечить журналирование в сервлете?
При работе с большим количеством подключений к базе данных рекомендуется инициализировать их в servlet context listener, а также установить в качестве атрибута контекста для возможности использования другими сервлетами.
Журналирование подключается к сервлету стандартным для логгера способом (например для log4j это может быть property-файл или XML-конфигурация) , а далее эта информация используется при настройке соответствующего context listener.
к оглавлению
Какие основные особенности появились в спецификации Servlet 3?
- Servlet Annotations. До Servlet 3 вся конфигурация содержалась в
web.xml
, что приводило к ошибкам и неудобству при работе с большим количестве сервлетов. Примеры аннотаций:@WebServlet
,@WebInitParam
,@WebFilter
,@WebListener
. - Web Fragments. Одностраничное веб приложение может содержать множество модулей: все модули прописываются в
fragment.xml
в папкеMETA-INF
. Это позволяет разделять веб приложение на отдельные модули, собранные как .jar-файлы в отдельнойlib
директории. - Динамическое добавление веб компонентов. Появилась возможность программно добавлять фильтры и слушатели, используя
ServletContext
объект. Для этого применяются методыaddServlet()
,addFilter()
,addListener()
. Используя это нововведение стало доступным построение динамической системы, в которой необходимый объект будет создан и вызван только по необходимости. - Асинхронное выполнение. Поддержка асинхронной обработки позволяет передать выполнение запроса в другой поток без удержания всего сервера занятым.
к оглавлению
Какие способы аутентификации доступны сервлету?
Спецификация сервлетов определяет четыре типа проверки подлинности:
- HTTP Basic Authentication —
BASIC
. При доступе к закрытым ресурсам появится окно, которое попросит ввести данные для аутентификации. - Form Based Login —
FORM
. Используется собственная html форма: - HTTP Digest Authentication —
DIGEST
. Цифровая аутентификация с шифрованием. - HTTPS Authentication —
CLIENT-CERT
. Аутентификация с помощью клиентского сертификата.
<login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.html</form-login-page> <form-error-page>/error.html</form-error-page> </form-login-config> </login-config>
к оглавлению
Что такое Java Server Pages (JSP)?
JSP (JavaServer Pages) — платформонезависимая переносимая и легко расширяемая технология разработки веб-приложений, позволяющая веб-разработчикам создавать содержимое, которое имеет как статические, так и динамические компоненты. Страница JSP содержит текст двух типов: статические исходные данные, которые могут быть оформлены в одном из текстовых форматов HTML, SVG, WML, или XML, и JSP-элементы, которые конструируют динамическое содержимое. Кроме этого могут использоваться библиотеки JSP-тегов, а также EL (Expression Language), для внедрения Java-кода в статичное содержимое JSP-страниц.
Код JSP-страницы транслируется в Java-код сервлета с помощью компилятора JSP-страниц Jasper, и затем компилируется в байт-код JVM.
JSP-страницы загружаются на сервере и управляются Java EE Web Application. Обычно такие страницы упакованы в файловые архивы .war и .ear.
к оглавлению
Зачем нужен JSP?
JSP расширяет технологию сервлетов обеспечивая возможность создания динамических страницы с HTML подобным синтаксисом.
Хотя создание представлений поддерживается и в сервлетах, но большая часть любой веб-страницы является статической, поэтому код сервлета в таком случае получается чересчур перегруженным, замусоренным и поэтому при его написании легко допустить ошибку.
Еще одним преимуществом JSP является горячее развертывание — возможность заменить одну страницу на другую непосредственно в контейнере без необходимости перекомпилировать весь проект или перезапускать сервер.
Однако рекомендуется избегать написания серьёзной бизнес-логики в JSP и использовать страницу только в качестве представления.
к оглавлению
Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
Когда пользователь переходит по ссылке на страницу page.jsp
, он отправляет http-запрос на сервер GET /page.jsp
. Затем, на основе этого запроса и текста самой страницы, сервер генерирует java-класс, компилирует его и выполняет полученный сервлет, формирующий ответ пользователю в виде представления этой страницы, который сервер и перенаправляет обратно пользователю.
к оглавлению
Расскажите об этапах (фазах) жизненного цикла JSP.
Если посмотреть код внутри созданной JSP страницы, то он будет выглядеть как HTML и не будет похож на java класс. Конвертацией JSP страниц в HTML код занимается контейнер, который так же создает и сервлет для использования в веб приложении.
Жизненный цикл JSP состоит из нескольких фаз, которыми руководит JSP контейнер:
- Translation – проверка и парсинг кода JSP страницы для создания кода сервлета.
- Compilation – компиляция исходного кода сервлета.
- Class Loading – загрузка скомпилированного класса в память.
- Instantiation – внедрение конструктора без параметра загруженного класса для инициализации в памяти.
- Initialization – вызов
init()
метода объекта JSP класса и инициализация конфигурации сервлета с первоначальными параметрами, которые указаны в дескрипторе развертывания (web.xml
). После этой фазы JSP способен обрабатывать запросы клиентов. Обычно эти фазы происходят после первого запроса клиента (т.е. ленивая загрузка), но можно настроить загрузку и инициализацию JSP на старте приложения по аналогии с сервлетами. - Request Processing – длительный жизненный цикл обработки запросов клиента JSP страницей. Обработка является многопоточной и аналогична сервлетам — для каждого запроса создается новый поток, объекты
ServletRequest
иServletResponse
, происходит выполнение сервис методов. - Destroy – последняя фаза жизненного цикла JSP, на которой её класс удаляется из памяти. Обычно это происходит при выключении сервера или выгрузке приложения.
к оглавлению
Расскажите о методах жизненного цикла JSP.
Контейнер сервлетов (например, Tomcat, GlassFish) создает из JSP-страницы класс сервлета, наследующего свойства интерфейса javax.servlet.jsp.HttpJspBase
и включающего следующие методы:
jspInit()
— метод объявлен в JSP странице и реализуется с помощью контейнера. Этот метод вызывается один раз в жизненном цикле JSP для того, чтобы инициализировать конфигурационные параметры указанные в дескрипторе развертывания. Этот метод можно переопределить с помощью определения элемента JSP scripting и указания необходимых параметров для инициализации;_jspService()
— метод переопределяется контейнером автоматически и соответствует непосредственно коду JSP, описанному на странице. Этот метод определен в интерфейсеHttpJspPage
, его имя начинается с нижнего подчеркивания и он отличается от других методов жизненного цикла тем, что его невозможно переопределить;jspDestroy()
— метод вызывается контейнером для удаления объекта из памяти (на последней фазе жизненного цикла JSP — Destroy). Метод вызывается только один раз и доступен для переопределения, предоставляя возможность освободить ресурсы, которые были созданы вjspInit()
.
к оглавлению
Какие методы жизненного цикла JSP могут быть переопределены?
Возможно переопределить лишь jspInit()
и jspDestroy()
методы.
к оглавлению
Как можно предотвратить прямой доступ к JSP странице из браузера?
Прямой доступ к директории /WEB-INF/
из веб-приложения отсутствует. Поэтому JSP-страницы можно расположить внутри этой папки и тем самым запретить доступ к странице из браузера. Однако, по аналогии с описанием сервлетов, будет необходимо настроить дескриптор развертывания:
<servlet> <servlet-name>Example</servlet-name> <jsp-file>/WEB-INF/example.jsp</jsp-file> <init-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Example</servlet-name> <url-pattern>/example.jsp</url-pattern> </servlet-mapping>
к оглавлению
Какая разница между динамическим и статическим содержимым JSP?
Статическое содержимое JSP (HTML, код JavaScript, изображения и т.д.) не изменяется в процессе работы веб приложения.
Динамические ресурсы созданы для того, чтобы отображать свое содержимое в зависимости от пользовательских действий. Обычно они представлены в виде выражений EL (Expression Language), библиотек JSP-тегов и пр.
к оглавлению
Как закомментировать код в JSP?
<!—- HTML комментарий; отображается на странице JSP —->
такие комментарии будут видны клиенту при просмотре кода страницы.<%—- JSP комментарий; не отображается на странице JSP —-%>
такие комментарии описываются в созданном сервлете и не посылаются клиенту. Для любых комментариев по коду или отладочной информации необходимо использовать именно такой тип комментариев.
к оглавлению
Какие существуют основные типы тегов JSP?
- Выражение JSP:
<%= expression %>
— выражение, которое будет обработано с перенаправлением результата на вывод; - Скриплет JSP:
<% code %>
— код, добавляемый в методservice()
. - Объявление JSP:
<%! code %>
— код, добавляемый в тело класса сервлета вне методаservice()
. - Директива JSP page:
<%@ page att="value" %>
— директивы для контейнера сервлетов с информацией о параметрах. - Директива JSP include:
<%@ include file="url" %>
— файл в локальной системе, подключаемый при трансляции JSP в сервлет. - Комментарий JSP:
<%-- comment --%>
— комментарий; игнорируется при трансляции JSP страницы в сервлет.
к оглавлению
Что вы знаете о действиях JSP (Action tag и JSP Action Elements).
Action tag и JSP Action Elements предоставляют методы работы с Java Beans, подключения ресурсов, проброса запросов и создания динамических XML элементов. Такие элементы всегда начинаются с записи jsp:
и используются непосредственно внутри страницы JSP без необходимости подключения сторонних библиотек или дополнительных настроек.
Наиболее часто используемыми JSP Action Elements являются:
jsp:include
:<jsp:include page="относительный URL" flush="true"/>
— подключить файл при запросе страницы. Если необходимо, чтобы файл подключался в процессе трансляции страницы, то используется директиваpage
совместно с атрибутомinclude
;jsp:useBean
:<jsp:useBean att=значение*/>
или<jsp:useBean att=значение*>...</jsp:useBean>
— найти или создать Java bean;jsp:setProperty
:<jsp:setProperty att=значение*/>
— установить свойства Java bean, или явно, или указанием на соответствующее значение, передаваемое при запросе;jsp:forward
:<jsp:forward page="относительный URL"/>
— передать запрос другой странице;jsp:plugin
:<jsp:plugin attribute="значение"*>...</jsp:plugin>
— сгенерировать (в зависимости от типа браузера) тэгиOBJECT
илиEMBED
для апплета, использующего технологию Java Plugin.
к оглавлению
Взаимодействие JSP — сервлет — JSP.
«JSP — сервлет — JSP» архитектура построения приложений носит название MVC (Model/View/Controller):
-
Model — классы данных и бизнес-логики;
-
View — страницы JSP;
-
Controller — сервлеты.
к оглавлению
Какие области видимости переменных существуют в JSP?
Область видимости объектов определяется тем контекстом, в который помещается данный объект. В зависимости от той или иной области действия так же определяется время существования объекта.
В JSP предусмотрены следующие области действия переменных (объектов):
request
область действия запроса — объект будет доступен на текущей JSP странице, странице пересылки (при использованииjsp:forward
) или на включаемой странице (при использованииjsp:include
);session
область действия сессии — объект будет помещен в сеанс пользователя, будет доступен на всех JSP страницах и будет существовать пока существует сессия пользователя, или он не будет из нее принудительно удален.application
область действия приложения — объект будет доступен для всех пользователей на всех JSP страницах и будет существовать на протяжении всей работы приложения или пока не будет удален принудительно и контекста приложения.page
область действия страницы — объект будет доступен только на той странице, где он определен. На включаемых (jsp:include
) и переадресуемых (jsp:forward
) страницах данный объект уже не будет доступен.
Таким образом, чтобы объект был доступен всем JSP страницам, необходимо указать область видимости application
или session
, в зависимости от того требуется ли доступ к объекту всем пользователям или только текущему.
Для указания требуемой области действия при определении объекта на JSP странице используется атрибут scope тега jsp:useBean
:
<jsp:useBean id="myBean" class="ru.javacore.MyBean" scope="session"/>
Если не указывать атрибут scope
, то по умолчанию задается область видимости страницы page
к оглавлению
Какие неявные, внутренние объекты и методы есть на JSP странице?
JSP implicit objects (неявные объекты) создаются контейнером при конвертации JSP страницы в код сервлета для помощи разработчикам. Эти объекты можно использовать напрямую в скриптлетах для передачи информации в сервис методы, однако мы не можем использовать неявные объекты в JSP Declaration, т.к. такой код пойдет на уровень класса.
Существует 9 видов неявных объектов, которые можно использовать прямо на JSP странице. Семь из них объявлены как локальные переменные в начале _jspService()
метода, а два оставшихся могут быть использованы как аргументы метода _jspService()
.
out Object
:
<strong>Current Time is</strong>: <% out.print(new Date()); %><br>
request Object
:
<strong>Request User-Agent</strong>: <%=request.getHeader("User-Agent") %><br>
response Object
:
<strong>Response</strong>: <%response.addCookie(new Cookie("Test","Value")); %>
config Object
:
<strong>User init param value</strong>: <%=config.getInitParameter("User") %><br>
application Object
:
<strong>User context param value</strong>: <%=application.getInitParameter("User") %><br>
session Object
:
<strong>User Session ID</strong>: <%=session.getId() %><br>
pageContext Object
:
<% pageContext.setAttribute("Test", "Test Value"); %>
<strong>PageContext attribute</strong>: {Name="Test",Value="<%=pageContext.getAttribute("Test") %>"}<br>
page Object
:
<strong>Generated Servlet Name</strong>: <%=page.getClass().getName() %>
exception Object
:
<strong>Exception occured</strong>: <%=exception %><br>
к оглавлению
Какие неявные объекты не доступны в обычной JSP странице?
Неявный объект исключений JSP недоступен в обычных JSP страницах и используется на страницах ошибок JSP (errorpage) только для того, чтобы перехватить исключение, выброшенное JSP страницей и далее предоставить какую-либо полезную информацию клиенту.
к оглавлению
Что вы знаете о PageContext
и какие преимущества его использования?
Неявный объект JSP — экземпляр класса javax.servlet.jsp.PageContext
предоставляет доступ ко всем пространствам имён, ассоциированным с JSP-страницей, а также к различным её атрибутам.
Остальные неявные объекты добавляются к pageContext
автоматически.
Класс PageContext
это абстрактный класс, а его экземпляр можно получить через вызов метода JspFactory.getPageContext()
, и освободить через вызов метода JspFactory.releasePageContext()
.
PageContext
обладает следующим набором особенностей и возможностей:
- единый API для обслуживания пространств имён различных областей видимости;
- несколько удобных API для доступа к различным
public
-объектам; - механизм получения
JspWriter
для вывода; - механизм обслуживания использования сессии страницей;
- механизм экспонирования («показа») атрибутов директивы
page
среде скриптинга; - механизмы направления или включения текущего запроса в другие компоненты приложения;
- механизм обработки процессов исключений на страницах ошибок errorpage;
к оглавлению
Как сконфигурировать параметры инициализации для JSP?
Параметры инициализации для JSP задаются в web.xml
файле аналогично сервлетам — элементами servlet
и servlet-mapping
. Единственным отличием будет указание местонахождения JSP страницы:
<servlet> <servlet-name>Example</servlet-name> <jsp-file>/WEB-INF/example.jsp</jsp-file> <init-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </init-param> </servlet>
к оглавлению
Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?
JSP страницы используются в основном для целей отображения представления (view), а вся бизнес-логика (controller) и модель (model) должны быть реализованы в сервлетах или классах-моделях. Обязанность JSP страницы — создание HTML ответа из переданных через атрибуты параметров. Большая часть JSP содержит HTML код, а для того, чтобы помочь верстальщикам понять JSP код страницы предоставляется функционал элементов action, JSP EL, JSP Standart Tag Library. Именно их и необходимо использовать вместо скриптлетов для создания моста между (JSP)HTML и (JSP)Java частями.
к оглавлению
Можно ли определить класс внутри JSP страницы?
Определить класс внутри JSP страницы можно, но это считается плохой практикой:
<%! private static class ExampleOne { //... } %> <% private class ExampleTwo { //... } %>
к оглавлению
Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?
JSP Expression Language (EL) — скриптовый язык выражений, который позволяет получить доступ к Java компонентам (JavaBeans) из JSP. Начиная с JSP 2.0 используется внутри JSP тегов для отделения Java кода от JSP для обеспечения лёгкого доступа к Java компонентам, уменьшая при этом количество кода Java в JSP-страницах, или даже полностью исключая его.
Развитие EL происходило с целью сделать его более простым для дизайнеров, которые имеют минимальные познания в языке программирования Java. До появления языка выражений, JSP имел несколько специальных тегов таких как скриптлеты (англ.), выражения и т. п. которые позволяли записывать Java код непосредственно на странице. С использованием языка выражений веб-дизайнер должен знать только то, как организовать вызов соответствующих java-методов.
Язык выражений JSP 2.0 включает:
- Создание и изменение переменных.
- Управление потоком выполнения программы: ветвление, выполнение различных типов итераций и т.д.
- Упрощенное обращение к встроенным JSP-объектам.
- Возможность создавать собственные функции.
Язык выражений используется внутри конструкции ${ ... }
. Подобная конструкция может размещаться либо отдельно, либо в правой части выражения установки атрибута тега.
к оглавлению
Какие типы EL операторов вы знаете?
Операторы в EL поддерживают наиболее часто используемые манипуляции данными.
Типы операторов:
- Стандартные операторы отношения:
==
(илиeq
),!=
(илиneq
),<
(илиlt
),>
(илиgt
),<=
(илиle
),>=
(илиge
). - Арифметические операторы:
+
,–
,*
,/
(илиdiv
),%
(илиmod
). - Логические операторы:
&&
(илиand
),||
(илиor
),!
(илиnot
). - Оператор
empty
– используется для проверки переменной наnull
, или «пустое значение», который зависит от типа проверяемого объекта. Например, нулевая длина для строки или нулевой размер для коллекции.
к оглавлению
Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.
Язык выражений JSP предоставляет множество неявных объектов, которые можно использовать для получения атрибутов в различных областях видимости (scopes) и для значений параметров. Важно отметить, что они отличаются от неявных объектов JSP и содержат атрибуты в заданной области видимости. Наиболее часто использующийся implicit object в JSP EL и JSP page — это объект pageContext. Ниже представлена таблица неявных объектов JSP EL.
к оглавлению
Как отключить возможность использования EL в JSP?
Для игнорирования выполнения языка выражений на странице существует два способа:
- использовать директиву
<%@ page isELIgnored = «true» %>
, - настроить
web.xml
(лучше подходит для отключения EL сразу на нескольких страницах):
<jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <el-ignored>true</el-ignored> </jsp-property-group> </jsp-config>
к оглавлению
Как узнать тип HTTP метода используя JSP EL?
${pageContext.request.method}
.
к оглавлению
Что такое JSTL (JSP Standard tag library)?
JavaServer Pages Standard Tag Library, JSTL, Стандартная библиотека тегов JSP — расширение спецификации JSP (конечный результат JSR 52), добавляющее библиотеку JSP тегов для общих нужд, таких как разбор XML данных, условная обработка, создание циклов и поддержка интернационализации.
JSTL является альтернативой такому виду встроенной в JSP логики, как скриплеты (прямые вставки Java кода). Использование стандартизованного множества тегов предпочтительнее, поскольку получаемый код легче поддерживать и проще отделять бизнес-логику от логики отображения.
Для использования JSTL тегов необходимо:
- подключить зависимости, например в
pom.xml
:
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency>
- указать пространство имен основных тегов JSTL через указание на JSP странице код:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
к оглавлению
Из каких групп тегов состоит библиотека JSTL?
Группы тегов JSTL согласно их функциональности:
- Core Tags предоставляют возможности итерации, обработки исключений, URL, forward, redirect response и т.д.
- Formatting Tags и Localization Tags предоставляют возможности по форматированию чисел, дат и поддержки i18n локализации и resource bundles.
- SQL Tags – поддержка работы с базами данных.
- XML Tags используются для работы с XML документами: парсинга, преобразования данных, выполнения выражений XPath и т.д..
- JSTL Functions Tags предоставляет набор функций, которые позволяют выполнять различные операции со строками и т.п. Например, по конкатенации или разбиению строк.
к оглавлению
Какая разница между <c:set>
и <jsp:useBean>
?
Оба тега создают и помещают экземпляры в заданную область видимости, но <jsp:useBean>
только создаёт экземпляр конкретного типа, а <c:set>
, создав экземпляр, позволяет дополнительно извлекать значение, например, из параметров запроса, сессии и т. д.
к оглавлению
Чем отличается <c:import>
от <jsp:include>
и директивы <%@include %>
?
По сравнению с action-тегом <jsp:include>
и директивой <%@include %>
тег <c:import>
обеспечивает более совершенное включение динамических ресурсов, т.к. получает доступ к источнику, чтение информации из которого происходит непосредственно без буферизации и контент включается в исходную JSP построчно.
к оглавлению
Как можно расширить функциональность JSP?
Что вы знаете о написании пользовательских JSP тегов?
Приведите пример использования собственных тегов.
JSP можно расширить с помощью создания собственных тегов с необходимой функциональностью, которые можно добавить в библиотеку тегов на страницу JSP указав необходимое пространство имен.
/WEB-INF/exampleTag.tld
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_1.xsd"> <tlib-version>1.0</tlib-version> <short-name>example</short-name> <uri>/WEB-INF/exampleTag</uri> <tag> <name>exampleTag</name> <tag-class>xyz.company.ExampleTag</tag-class> <body-content>empty</body-content> <info>The example tag displays Hello World!</info> </tag> </taglib>
xyz.company.ExampleServlet.java
package xyz.company; import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class ExampleTag extends TagSupport{ private static final long serialVersionUID = 1L; @Override public int doStartTag() throws JspException { try { pageContext.getOut().print("Hello World!"); } catch(IOException ioException) { throw new JspException("Error: " + ioException.getMessage()); } return SKIP_BODY; } }
exampleTag.jsp
<%@ taglib uri="/WEB-INF/exampleTag.tld" prefix="example"%>
<%@ page session="false" pageEncoding="UTF-8"%>
<html>
<head>
<title>Example Tag</title>
</head>
<body>
<h1>Example Tag</h1>
<p><example:exampleTage /><p>
</body>
</html>
Также в пользовательских тегах существует возможность задать входные параметры. Например, существует необходимость отформатировать каким-либо стилем очень длинное число. Для этого можно использовать собственный тег по типу:
<mytags:formatNumber number="123456.789" format="#,## #.00"/>
Используя входные параметры, число должно быть преобразовано на JSP странице в таком виде 123,456.79
согласно шаблону. Т.к. JSTL не предоставляет такой функциональности, необходимо создать пользовательский тег для получения необходимого результата.
к оглавлению
Как сделать перенос строки в HTML средствами JSP?
Для переноса строки можно использовать тег c:out
и атрибут escapeXml
, который отключает обработку HTML элементов. В этом случае браузер получит следующий код в виде строки и обработает элемент <br>
как требуется:
<c:out value="<br> creates a new line in HTML" escapeXml="true"></c:out>
к оглавлению
Почему не нужно конфигурировать стандартные JSP теги в web.xml
?
Стандартные теги JSP не конфигурируются в web.xml
, потому что tld файлы уже находятся внутри каталога /META-INF/
в jar файлах JSTL.
Когда контейнер загружает веб-приложение и находит tld файлы в в jar файле в директории /META-INF/
, то он автоматически настраивает их для непосредственного использования на JSP страницах. Остается только задать пространство имен на JSP странице.
к оглавлению
Как можно обработать ошибки JSP страниц?
Для обработки исключений выброшенных на JSP странице достаточно лишь задать страницу ошибки JSP и при её создании установить значение page directive attribute isErrorPage
в значение true
. Таким образом будет предоставлен доступ к неявным объектам исключений в JSP и появится возможность передавать собственные, более информативные сообщения об ошибках клиенту. При этом настройка дескриптора развертывания выглядит так:
<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error.jsp</location> </error-page>
к оглавлению
Как происходит обработка ошибок с помощью JSTL?
Для перехвата и обработки исключений в служебных методах класса служат JSTL Core Tags c:catch
и c:if
.
Тег
c:catch
перехватывает исключение и обертывает его в переменнуюexception
, доступную для обработки в тегеc:if
:
<c:catch var ="exception">
<% int x = 42/0;%>
</c:catch>
<c:if test = "${exception ne null}">
<p>Exception is : ${exception} <br />
Exception Message: ${exception.message}</p>
</c:if>
Обратите внимание что используется язык выражений JSP EL в теге c:if
.
к оглавлению
Как конфигурируется JSP в дескрипторе развертывания.
Для настройки различных параметров JSP страниц используется элемент jsp-config
, который отвечает за:
- управление элементами скриптлетов на странице;
- управления выполнением в языке выражений;
- определение шаблона URL для encoding;
- определение размера буфера, который используется для объектов на странице;
- обозначение групп ресурсов, соответствующих шаблону URL, которые должны быть обработаны как XML документ.
<jsp-config> <taglib> <taglib-uri>http://company.xyz/jsp/tlds/customtags</taglib-uri> <taglib-location>/WEB-INF/exampleTag.tld</taglib-location> </taglib> </jsp-config>
к оглавлению
Можно ли использовать Javascript на JSP странице?
Да, это возможно. Несмотря на то, что JSP это серверная технология, на выходе она всё равно создает HTML
страницу, на которую можно добавлять Javascript и CSS.
к оглавлению
Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?
Jsp-страница, по умолчанию, всегда создает сессию. Используя директиву page
с атрибутом session
можно изменить это поведение:
<%@ page session ="false" %>
к оглавлению
Какая разница между JSPWriter
и сервлетным PrintWriter
?
PrintWriter
является объектом отвечающим за запись содержания ответа на запрос. JspWriter
использует объект PrintWriter
для буферизации. Когда буфер заполняется или сбрасывается, JspWriter
использует объект PrintWriter
для записи содержания в ответ.
к оглавлению
Опишите общие практические принципы работы с JSP.
Хорошей практикой работы с технологией JSP является соблюдение следующих правил:
- Следует избегать использования элементов скриптлетов на странице. Если элементы action, JSTL, JSP EL не удовлетворяют потребностям, то желательно написать собственный тег.
- Рекомендуется использовать разные виды комментариев: так JSP комментарии необходимы для уровня кода и отладки, т.к. они не будут показаны клиенту.
- Не стоит размещать какой-либо бизнес логики внутри JSP страницы. Страницы должны использоваться только для создания ответов клиенту.
- Для повышения производительности лучше отключать создание сессии на странице, когда это не требуется.
- Директивы
taglib
,page
в начале JSP страницы улучшают читабельность кода. - Следует правильно использовать директиву
include
и элементjsp:include action
. Первая используется для статических ресурсов, а второй для динамических ресурсов времени выполнения. - Обработку исключений нужно производить с помощью страниц ошибок. Это помогает избегать запуска специальных служебных методов и может повысить производительность.
- Использующиеся CSS и JavaScript должны быть разнесены в разные файлы и подключаться в начале страницы.
- В большинстве случаев JSTL должно хватать для всех нужд. Если это не так, то в начале следует проанализировать логику своего приложения, и попробовать перенести выполнения кода в сервлет, а далее с помощью установки атрибутов использовать на JSP странице только результат.
к оглавлению
Источники
- javastudy.ru
- java2ee.ru
- Java-online
- Codenet
- JavaTalks Articles
Вопросы для собеседования
Программирование, JAVA, Параллельное программирование, GitHub, Eclipse
Рекомендация: подборка платных и бесплатных курсов Python — https://katalog-kursov.ru/
Цель публикации показать начинающим Java программистам все этапы создания многопоточного сервера. Для полного понимания данной темы основная информация содержится в комментариях моего кода и в выводимых в консоли сообщениях для лучшего понимания что именно происходит и в какой именно последовательности.
В начале будет рассмотрено создание элементарного клиент-сервера, для усвоения базовых знаний, на основе которых будет строиться многопоточная архитектура.
Понятия.
— Потоки: для того чтобы не перепутать что именно подразумевается под потоком я буду использовать существующий в профессиональной литературе синоним — нить, чтобы не путать Stream и Thread, всё-таки более профессионально выражаться — нить, говоря про Thread.
— Сокеты(Sockets): данное понятие тоже не однозначно, поскольку в какой-то момент сервер выполняет — клиентские действия, а клиент — серверные. Поэтому я разделил понятие серверного сокета — (ServerSocket) и сокета (Socket) через который практически осуществляется общение, его будем называть сокет общения, чтобы было понятно о чём речь.
Кроме того сокетов общения создаётся по одному на каждом из обменивающихся данными приложении, поэтому сокет приложения которое имеет у себя объект - ServerSocket и первоначально открывает порт в ожидании подключения будем называть сокет общения на стороне сервера, а сокет который создаёт подключающееся к порту по известному адресу второе приложение будем называть сокетом общения на стороне клиента.
Спасибо за подсказку про Thread.sleep();!
Конечно в реальном коде Thread.sleep(); устанавливать не нужно — это муветон! В данной публикации я его использую только для того чтобы выполнение программы было нагляднее, что бы успевать разобраться в происходящем.
Так что тестируйте, изучайте и в своём коде никогда не используйте Thread.sleep();!
Оглавление:
1) Однопоточный элементарный сервер.
2) Клиент.
3) Многопоточный сервер – сам по себе этот сервер не участвует в общении напрямую, а лишь является фабрикой однонитевых делегатов(делегированных для ведения диалога с клиентами серверов) для общения с вновь подключившимися клиентами, которые закрываются после окончания общения с клиентом.
4) Имитация множественного обращения клиентов к серверу.
По многочисленным замечаниям выкладываю ссылку на исходники на GitHub:
(https://github.com/merceneryinbox/Clietn-Server_Step-by-step.git)
Итак, начнём с изучения структуры однопоточного сервер, который может принять только одного клиента для диалога. Код приводимый ниже необходимо запускать в своей IDE в этом идея всей статьи. Предлагаю все детали уяснить из подробно задокументированного кода ниже:
- 1) Однопоточный элементарный сервер.
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TestAsServer {
/**
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// стартуем сервер на порту 3345
try (ServerSocket server= new ServerSocket(3345)){
// становимся в ожидание подключения к сокету под именем - "client" на серверной стороне
Socket client = server.accept();
// после хэндшейкинга сервер ассоциирует подключающегося клиента с этим сокетом-соединением
System.out.print("Connection accepted.");
// инициируем каналы для общения в сокете, для сервера
// канал записи в сокет
DataOutputStream out = new DataOutputStream(client.getOutputStream());
System.out.println("DataOutputStream created");
// канал чтения из сокета
DataInputStream in = new DataInputStream(client.getInputStream());
System.out.println("DataInputStream created");
// начинаем диалог с подключенным клиентом в цикле, пока сокет не закрыт
while(!client.isClosed()){
System.out.println("Server reading from channel");
// сервер ждёт в канале чтения (inputstream) получения данных клиента
String entry = in.readUTF();
// после получения данных считывает их
System.out.println("READ from client message - "+entry);
// и выводит в консоль
System.out.println("Server try writing to channel");
// инициализация проверки условия продолжения работы с клиентом по этому сокету по кодовому слову - quit
if(entry.equalsIgnoreCase("quit")){
System.out.println("Client initialize connections suicide ...");
out.writeUTF("Server reply - "+entry + " - OK");
Thread.sleep(3000);
break;
}
// если условие окончания работы не верно - продолжаем работу - отправляем эхо-ответ обратно клиенту
out.writeUTF("Server reply - "+entry + " - OK");
System.out.println("Server Wrote message to client.");
// освобождаем буфер сетевых сообщений (по умолчанию сообщение не сразу отправляется в сеть, а сначала накапливается в специальном буфере сообщений, размер которого определяется конкретными настройками в системе, а метод - flush() отправляет сообщение не дожидаясь наполнения буфера согласно настройкам системы
out.flush();
}
// если условие выхода - верно выключаем соединения
System.out.println("Client disconnected");
System.out.println("Closing connections & channels.");
// закрываем сначала каналы сокета !
in.close();
out.close();
// потом закрываем сам сокет общения на стороне сервера!
client.close();
// потом закрываем сокет сервера который создаёт сокеты общения
// хотя при многопоточном применении его закрывать не нужно
// для возможности поставить этот серверный сокет обратно в ожидание нового подключения
System.out.println("Closing connections & channels - DONE.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 2) Клиент.
Сервер запущен и находится в блокирующем ожидании server.accept(); обращения к нему с запросом на подключение. Теперь можно подключаться клиенту, напишем код клиента и запустим его. Клиент работает когда пользователь вводит что-либо в его консоли (внимание! в данном случае сервер и клиент запускаются на одном компьютере с локальным адресом — localhost, поэтому при вводе строк, которые должен отправлять клиент не забудьте убедиться, что вы переключились в рабочую консоль клиента!).
После ввода строки в консоль клиента и нажатия enter строка проверяется не ввёл ли клиент кодовое слово для окончания общения дальше отправляется серверу, где он читает её и то же проверяет на наличие кодового слова выхода. Оба и клиент и сервер получив кодовое слово закрывают ресурсы после предварительных приготовлений и завершают свою работу.
Посмотрим как это выглядит в коде:
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
public class TestASClient {
/**
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// запускаем подключение сокета по известным координатам и нициализируем приём сообщений с консоли клиента
try(Socket socket = new Socket("localhost", 3345);
BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
DataInputStream ois = new DataInputStream(socket.getInputStream()); )
{
System.out.println("Client connected to socket.");
System.out.println();
System.out.println("Client writing channel = oos & reading channel = ois initialized.");
// проверяем живой ли канал и работаем если живой
while(!socket.isOutputShutdown()){
// ждём консоли клиента на предмет появления в ней данных
if(br.ready()){
// данные появились - работаем
System.out.println("Client start writing in channel...");
Thread.sleep(1000);
String clientCommand = br.readLine();
// пишем данные с консоли в канал сокета для сервера
oos.writeUTF(clientCommand);
oos.flush();
System.out.println("Clien sent message " + clientCommand + " to server.");
Thread.sleep(1000);
// ждём чтобы сервер успел прочесть сообщение из сокета и ответить
// проверяем условие выхода из соединения
if(clientCommand.equalsIgnoreCase("quit")){
// если условие выхода достигнуто разъединяемся
System.out.println("Client kill connections");
Thread.sleep(2000);
// смотрим что нам ответил сервер на последок перед закрытием ресурсов
if(ois.available()!=0) {
System.out.println("reading...");
String in = ois.readUTF();
System.out.println(in);
}
// после предварительных приготовлений выходим из цикла записи чтения
break;
}
// если условие разъединения не достигнуто продолжаем работу
System.out.println("Client sent message & start waiting for data from server...");
Thread.sleep(2000);
// проверяем, что нам ответит сервер на сообщение(за предоставленное ему время в паузе он должен был успеть ответить)
if(ois.available()!=0) {
// если успел забираем ответ из канала сервера в сокете и сохраняем её в ois переменную, печатаем на свою клиентскую консоль
System.out.println("reading...");
String in = ois.readUTF();
System.out.println(in);
}
}
}
// на выходе из цикла общения закрываем свои ресурсы
System.out.println("Closing connections & channels on clentSide - DONE.");
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 3) Многопоточный сервер
А что если к серверу хочет подключиться ещё один клиент!? Ведь описанный выше сервер либо находится в ожидании подключения одного клиента, либо общается с ним до завершения соединения, что делать остальным клиентам? Для такого случая нужно создать фабрику которая будет создавать описанных выше серверов при подключении к сокету новых клиентов и не дожидаясь пока делегированный подсервер закончит диалог с клиентом откроет accept() в ожидании следующего клиента. Но чтобы на серверной машине хватило ресурсов для общения со множеством клиентов нужно ограничить количество возможных подключений. Фабрика будет выдавать немного модифицированный вариант предыдущего сервера(модификация будет касаться того что класс сервера для фабрики будет имплементировать интерфейс — Runnable для возможности его использования в пуле нитей — ExecuteServices). Давайте создадим такую серверную фабрику и ознакомимся с подробным описанием её работы в коде:
- Фабрика:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author mercenery
*
*/
public class MultiThreadServer {
static ExecutorService executeIt = Executors.newFixedThreadPool(2);
/**
* @param args
*/
public static void main(String[] args) {
// стартуем сервер на порту 3345 и инициализируем переменную для обработки консольных команд с самого сервера
try (ServerSocket server = new ServerSocket(3345);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
System.out.println("Server socket created, command console reader for listen to server commands");
// стартуем цикл при условии что серверный сокет не закрыт
while (!server.isClosed()) {
// проверяем поступившие комманды из консоли сервера если такие
// были
if (br.ready()) {
System.out.println("Main Server found any messages in channel, let's look at them.");
// если команда - quit то инициализируем закрытие сервера и
// выход из цикла раздачии нитей монопоточных серверов
String serverCommand = br.readLine();
if (serverCommand.equalsIgnoreCase("quit")) {
System.out.println("Main Server initiate exiting...");
server.close();
break;
}
}
// если комманд от сервера нет то становимся в ожидание
// подключения к сокету общения под именем - "clientDialog" на
// серверной стороне
Socket client = server.accept();
// после получения запроса на подключение сервер создаёт сокет
// для общения с клиентом и отправляет его в отдельную нить
// в Runnable(при необходимости можно создать Callable)
// монопоточную нить = сервер - MonoThreadClientHandler и тот
// продолжает общение от лица сервера
executeIt.execute(new MonoThreadClientHandler(client));
System.out.print("Connection accepted.");
}
// закрытие пула нитей после завершения работы всех нитей
executeIt.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Модифицированный Runnable сервер для запуска из предыдущего кода:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class MonoThreadClientHandler implements Runnable {
private static Socket clientDialog;
public MonoThreadClientHandler(Socket client) {
MonoThreadClientHandler.clientDialog = client;
}
@Override
public void run() {
try {
// инициируем каналы общения в сокете, для сервера
// канал записи в сокет следует инициализировать сначала канал чтения для избежания блокировки выполнения программы на ожидании заголовка в сокете
DataOutputStream out = new DataOutputStream(clientDialog.getOutputStream());
// канал чтения из сокета
DataInputStream in = new DataInputStream(clientDialog.getInputStream());
System.out.println("DataInputStream created");
System.out.println("DataOutputStream created");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// основная рабочая часть //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// начинаем диалог с подключенным клиентом в цикле, пока сокет не
// закрыт клиентом
while (!clientDialog.isClosed()) {
System.out.println("Server reading from channel");
// серверная нить ждёт в канале чтения (inputstream) получения
// данных клиента после получения данных считывает их
String entry = in.readUTF();
// и выводит в консоль
System.out.println("READ from clientDialog message - " + entry);
// инициализация проверки условия продолжения работы с клиентом
// по этому сокету по кодовому слову - quit в любом регистре
if (entry.equalsIgnoreCase("quit")) {
// если кодовое слово получено то инициализируется закрытие
// серверной нити
System.out.println("Client initialize connections suicide ...");
out.writeUTF("Server reply - " + entry + " - OK");
Thread.sleep(3000);
break;
}
// если условие окончания работы не верно - продолжаем работу -
// отправляем эхо обратно клиенту
System.out.println("Server try writing to channel");
out.writeUTF("Server reply - " + entry + " - OK");
System.out.println("Server Wrote message to clientDialog.");
// освобождаем буфер сетевых сообщений
out.flush();
// возвращаемся в началло для считывания нового сообщения
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// основная рабочая часть //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// если условие выхода - верно выключаем соединения
System.out.println("Client disconnected");
System.out.println("Closing connections & channels.");
// закрываем сначала каналы сокета !
in.close();
out.close();
// потом закрываем сокет общения с клиентом в нити моносервера
clientDialog.close();
System.out.println("Closing connections & channels - DONE.");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Для имитации множественного обращения клиентов к серверу, создадим и запустим (после запуска серверной части) фабрику Runnable клиентов которые будут подключаться серверу и писать сообщения в цикле:
- 4) Имитация множественного обращения клиентов к серверу.
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
// private static ServerSocket server;
public static void main(String[] args) throws IOException, InterruptedException {
// запустим пул нитей в которых колличество возможных нитей ограничено -
// 10-ю.
ExecutorService exec = Executors.newFixedThreadPool(10);
int j = 0;
// стартуем цикл в котором с паузой в 10 милисекунд стартуем Runnable
// клиентов,
// которые пишут какое-то количество сообщений
while (j < 10) {
j++;
exec.execute(new TestRunnableClientTester());
Thread.sleep(10);
}
// закрываем фабрику
exec.shutdown();
}
}
Как видно из предыдущего кода фабрика запускает — TestRunnableClientTester() клиентов, напишем для них код и после этого запустим саму фабрику, чтобы ей было кого исполнять в своём пуле:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class TestRunnableClientTester implements Runnable {
static Socket socket;
public TestRunnableClientTester() {
try {
// создаём сокет общения на стороне клиента в конструкторе объекта
socket = new Socket("localhost", 3345);
System.out.println("Client connected to socket");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try (
// создаём объект для записи строк в созданный скокет, для
// чтения строк из сокета
// в try-with-resources стиле
DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
DataInputStream ois = new DataInputStream(socket.getInputStream())) {
System.out.println("Client oos & ois initialized");
int i = 0;
// создаём рабочий цикл
while (i < 5) {
// пишем сообщение автогенерируемое циклом клиента в канал
// сокета для сервера
oos.writeUTF("clientCommand " + i);
// проталкиваем сообщение из буфера сетевых сообщений в канал
oos.flush();
// ждём чтобы сервер успел прочесть сообщение из сокета и
// ответить
Thread.sleep(10);
System.out.println("Client wrote & start waiting for data from server...");
// забираем ответ из канала сервера в сокете
// клиента и сохраняем её в ois переменную, печатаем на
// консоль
System.out.println("reading...");
String in = ois.readUTF();
System.out.println(in);
i++;
Thread.sleep(5000);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Запускайте, вносите изменения в код, только так на самом деле можно понять работу этой структуры.
Спасибо за внимание.
Improve Article
Save Article
Like Article
Improve Article
Save Article
Like Article
Prerequisites: Socket Programming in Java
Multithreaded Server: A server having more than one thread is known as Multithreaded Server. When a client sends the request, a thread is generated through which a user can communicate with the server. We need to generate multiple threads to accept multiple requests from multiple clients at the same time.
Advantages of Multithreaded Server:
- Quick and Efficient: Multithreaded server could respond efficiently and quickly to the increasing client queries quickly.
- Waiting time for users decreases: In a single-threaded server, other users had to wait until the running process gets completed but in multithreaded servers, all users can get a response at a single time so no user has to wait for other processes to finish.
- Threads are independent of each other: There is no relation between any two threads. When a client is connected a new thread is generated every time.
- The issue in one thread does not affect other threads: If any error occurs in any of the threads then no other thread is disturbed, all other processes keep running normally. In a single-threaded server, every other client had to wait if any problem occurs in the thread.
Disadvantages of Multithreaded Server:
- Complicated Code: It is difficult to write the code of the multithreaded server. These programs can not be created easily
- Debugging is difficult: Analyzing the main reason and origin of the error is difficult.
Quick Overview
We create two java files, Client.java and Server.java. Client file contains only one class Client (for creating a client). Server file has two classes, Server(creates a server) and ClientHandler(handles clients using multithreading).
Client-Side Program: A client can communicate with a server using this code. This involves
- Establish a Socket Connection
- Communication
Java
import
java.io.*;
import
java.net.*;
import
java.util.*;
class
Client {
public
static
void
main(String[] args)
{
try
(Socket socket =
new
Socket(
"localhost"
,
1234
)) {
PrintWriter out =
new
PrintWriter(
socket.getOutputStream(),
true
);
BufferedReader in
=
new
BufferedReader(
new
InputStreamReader(
socket.getInputStream()));
Scanner sc =
new
Scanner(System.in);
String line =
null
;
while
(!
"exit"
.equalsIgnoreCase(line)) {
line = sc.nextLine();
out.println(line);
out.flush();
System.out.println(
"Server replied "
+ in.readLine());
}
sc.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
Server-Side Program: When a new client is connected, and he sends the message to the server.
1. Server class: The steps involved on the server side are similar to the article Socket Programming in Java with a slight change to create the thread object after obtaining the streams and port number.
- Establishing the Connection: Server socket object is initialized and inside a while loop a socket object continuously accepts an incoming connection.
- Obtaining the Streams: The inputstream object and outputstream object is extracted from the current requests’ socket object.
- Creating a handler object: After obtaining the streams and port number, a new clientHandler object (the above class) is created with these parameters.
- Invoking the start() method: The start() method is invoked on this newly created thread object.
2. ClientHandler class: As we will be using separate threads for each request, let’s understand the working and implementation of the ClientHandler class implementing Runnable. An object of this class acts as a Runnable target for a new thread.
- First, this class implements Runnable interface so that it can be passed as a Runnable target while creating a new Thread.
- Secondly, the constructor of this class takes a parameter, which can uniquely identify any incoming request, i.e. a Socket.
- Inside the run() method of this class, it reads the client’s message and replies.
Java
import
java.io.*;
import
java.net.*;
class
Server {
public
static
void
main(String[] args)
{
ServerSocket server =
null
;
try
{
server =
new
ServerSocket(
1234
);
server.setReuseAddress(
true
);
while
(
true
) {
Socket client = server.accept();
System.out.println(
"New client connected"
+ client.getInetAddress()
.getHostAddress());
ClientHandler clientSock
=
new
ClientHandler(client);
new
Thread(clientSock).start();
}
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
if
(server !=
null
) {
try
{
server.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
}
private
static
class
ClientHandler
implements
Runnable {
private
final
Socket clientSocket;
public
ClientHandler(Socket socket)
{
this
.clientSocket = socket;
}
public
void
run()
{
PrintWriter out =
null
;
BufferedReader in =
null
;
try
{
out =
new
PrintWriter(
clientSocket.getOutputStream(),
true
);
in =
new
BufferedReader(
new
InputStreamReader(
clientSocket.getInputStream()));
String line;
while
((line = in.readLine()) !=
null
) {
System.out.printf(
" Sent from the client: %sn"
,
line);
out.println(line);
}
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
try
{
if
(out !=
null
) {
out.close();
}
if
(in !=
null
) {
in.close();
clientSocket.close();
}
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
}
}
Steps:
- Compile both Client and Server programs.
- Run the server first and then the Client.
Output
Last Updated :
09 Nov, 2020
Like Article
Save Article
В процессе автоматизации деятельности предприятия при помощи 1С: Предприятие часто
возникают задачи интеграции и обмена с оборудованием и другими сторонними информационными
системами, например, банками, веб-сайтами, информационными системами партнеров.
Традиционно 1С: Предприятие выступает в качестве потребителя услуг, и реже – в качестве
поставщика. До этого момента у разработчиков популярными технологиями при получении
информации от 1C были COM и веб-сервисы, которые появились только в версии 8.1.
У обеих технологий (COM и веб-сервисы) есть свои минусы. Главные недостатки заключаются
в следующем. COM-технология позволяет в каждый момент времени выполнять только один
запрос. Обращение возможно только внутри своей локальной сети. Обработка одновременных
запросов для COM возможна, но требует затрат на организацию пула соединений и отладку
многопоточного приложения. Веб-сервисы же сложны по настройке и негибкие для программирования:
жестко привязаны к SOAP-стандартам. Подключение устройств, обменивающихся простыми
пакетами, работающих по http-протоколу, невозможно.
Новая идея организации веб-сервера внутри 1С, предложенная в статье, опирается на
проверенную временем богатую функциональность .Net Framework. Решение на основе
идеи лишены недостатков COM и веб-сервисов 1С. По сравнению с COM http-сервер можно
использовать вне локальной сети, поддерживается одновременная обработка нескольких
запросов. По сравнению с веб-сервисами 1С решение на базе http-сервера обладает
большей гибкостью, так как программист волен сам выбирать формат ответа сервера
(в том числе HTML, JSON, графические изображения, RSS и т.д.), а также контролировать
при ответе URL-адрес, идентификацию пользователей, коды ошибок, куки, кодировку,
осуществлять кеширование. Настройка же http-сервера внутри 1С сводится к простому
запуску внешней обработки.
Описание примера
Пример, приложенный к статье, состоит из двух файлов: http-сервера (внешняя обработка
1С 8.2 HttpServer82) и тестового приложения эмуляции одновременных запросов к серверу
(внешняя обработка 1С 8.2 TestHttpServer82). Обе обработки выполнены на основе управляемых
форм. По умолчанию оба приложения настроены на работу с портом 8082.
Сервер и тестовое приложение разработаны на 1С: Предприятие 8.2 и используют .Net
Framework 4.0 и компонент Elisy .Net Bridge 4. Соответственно, для работы примера
требуется установленный .Net Framework 4.0 и версия библиотеки Elisy .Net Bridge
v.4.0.2.0 и выше. Elisy .Net Bridge позволяет гармонично использовать классы и технологии
.Net Framework на 1С, ведущую роль оставляя 1С.
Для проверки работы достаточно запустить внешнюю обработку HttpServer82.epf из 1С: Предприятие
8.2. Если у вас Windows с включенным UAC, то запустить 1С: Предприятие необходимо
под администратором, иначе у приложения не будет достаточно прав на прослушивание
запросов.
Внешняя обработка позволяет задать порт, по которому будет осуществляться прослушивание
и число создаваемых потоков для обработки одновременных запросов. По умолчанию установлен
порт 8082 и 50 потоков.
После нажатия на кнопку Старт сервер переходит в рабочее состояние и осуществляет
обработку запросов на заданный порт. Например, теперь можно обратиться из вашего
браузера по адресу localhost:8082 и открыть
страницу, которую вернет сервер. В запросе же можно передавать параметры, например,
так: localhost:8082/test?x=1
Для проверки сервера в многопоточном режиме придумана внешняя обработка TestHttpServer82.epf,
которая осуществляет одновременный запуск запросов в цикле. В основу обработки для
организации параллельной работы положена замечательная технология PLINQ (Parallel
LINQ) из .Net framework 4.
Запускать тестовую обработку TestHttpServer82.epf следует из отдельного от сервера
сеанса 1С: Предприятие, иначе одновременный запуск в одном сеансе двух обработок
приведет к зависанию. В качестве параметров тестового примера выступают адрес запроса,
число одновременных запросов и число циклов. По умолчанию запуск алгоритма приведет
к 3 циклам обращений по адресу localhost:8082
с 20 одновременными запросами (надо отметить, что число одновременных запросов ограничено
числом ядер процессора).
Принцип работы
.Net framework предлагает своим разработчикам класс HttpListener, отвечающий за прослушивание http-протокола. Используя HttpListener,
вы можете создать прослушивание http-трафика, которое отвечает на http-запросы.
Вы можете использовать этот класс только на операционных системах Windows XP SP2
или Windows Server 2003 и выше. Попытка использования класса на более ранних системах
вызовет исключение.
Ниже приведен пример кода для 1С, который инициализирует объект типа HttpListener,
настраивая его на прослушивание всех URL по порту 8082. При запуске в 1С работа
программы приостанавливается, пока не последует запрос на порт, например, из браузера.
Как только вы пошлете из браузера запрос, например, 127.0.0.1:8082/ 1С вернет описанную в программе html-строку.
За что ответственен каждый участок кода, можно понять из комментариев, приведенных
внутри исходного кода.
ПодключитьВнешнююКомпоненту("Elisy.NetBridge4");
AddIn = New("AddIn.ElisyNetBridge4");
net = AddIn.GetNet();
Если НЕ net.GetStatic("System.Net.HttpListener","IsSupported") Тогда
Сообщить("Для класса HttpListener нужна ОС Windows XP SP2/2003 и выше");
Возврат;
КонецЕсли;
listener = net.New("System.Net.HttpListener");
listener.Prefixes.Add("http://*:8082/");
listener.Start();
Сообщить("Прослушивание...");
//Метод GetContext блокирует выполнение программы пока ждет запрос.
context = listener.GetContext();
request = context.Request;
// Получить объект ответа
response = context.Response;
// Создать ответ - HTML-строку
responseString = "Ответ от HttpListener";
buffer = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(responseString);
// Получить поток ответа и записать ответ в него.
response.ContentLength64 = buffer.Length;
output = response.OutputStream;
output.Write(buffer,0,buffer.Length);
// Необходимо закрыть выходной поток и остановить прослушивание
output.Close();
listener.Stop();
Это самый простой пример. Главный смысл простейшего примера в том, что 1С превращается
в полнофункциональный http-сервер, а 1С-программист получает инструмент для гибкой
настройки ответа с сервера. Он может вернуть его в любом формате (html, рисунок
или JSON), затребовать идентификацию пользователя или вообще вернуть ошибку. Но
вместе с тем есть недостатки: блокирование всего приложения, обработка только одного
запроса. Пример к статье содержит более сложный код, за счет чего устранены недостатки
простейшего примера из листинга.
От простого к сложному
В готовый пример вошел видоизмененный код работы с HttpListener, в котором вызов
метода listener.GetContext() заменен вызовом асинхронных аналогов listener.BeginGetContext()
и listener.EndGetContext(). Кроме этого создаются N отдельных потоков и заложена
синхронизация между потоками с вызовом кода-обработки запроса на стороне 1С: Предприятие.
Достоинством предлагаемой реализации http-сервера является возможность одновременной
обработки N запросов в разных потоках с передачей логики обработки в метод формы
1С: Предприятие. Пример при каждом запросе возвращает html с описанием источника
и URL запроса. Решение очень гибкое, так как все доработки можно делать прямо из
конфигуратора 1С. Никакие дополнительные проекты (например, C# или VB.Net) не задействованы.
Пример условно можно разделить на 2 части: код, который выполняется на стороне 1С: Предприятие
и код, который выполняется на стороне .Net framework. При этом .Net framework взял
на себя все, что нельзя реализовать средствами 1С: Предприятие, например, создание
и синхронизацию потоков.
Http-сервер оформлен в виде управляемой формы. При нажатии на кнопку Старт происходит
создание объектов классов HttpServer и Helper. Оба класса описываются на C# в макете
обработки ИсходныйКод и компилируются «на лету» в обработчике ПриОткрытии формы.
Класс Helper отвечает за перенаправление .Net-события в функцию ОбработатьЗапрос
на форме 1С и формирование сообщение об ошибке.
Опустив не относящиеся к теме задачи по инициализации формы и вспомогательных классов,
следует остановиться на методе ОбработатьЗапрос, который отвечает за возврат результата
http-клиенту. Вызывается обработчик из класса Helper, в качестве параметра принимает
объект контекста, может возвратить сообщение об ошибке, которое будет возвращено
клиенту.
Объект контекста типа HttpListenerContext, передаваемый в метод ОбработатьЗапрос
содержит два важных свойства: Request и Response, отвечающие за информацию о запросе
и ответе соответственно.
Свойство Request позволяет получить параметры и содержимое, переданные клиентом
при запросе. Информация о запросе, помещенная в свойство Request содержит такие
основные свойства как:
AcceptTypes – MIME-типы, поддерживаемые клиентом
ContentEncoding – информация о кодировке при ответе
Headers – набор заголовков
HttpMethod – метод HTTP, определенный клиентом
InputStream – поток, содержащий данные тела, пришедшие от клиента
IsAuthenticated – булево значение, показывающее идентифицирован ли пользователь
IsLocal – значение, показывающее локальный ли запрос (через localhost)
QueryString – строка запроса из запроса
RawUrl – информация об URL без хоста и порта
UrlReferrer – URL ресурса-источник данного перехода
UserAgent – информация о агенте-браузере пользователя
Через свойство Response осуществляется возврат контента клиенту, передача информации
об ошибке или перенаправление. Сопровождаться все может выставлением необходимых
заголовков. Многие свойства ответа (Response) схожи со свойствами запроса. Значимые
среди них следующие:
ContentEncoding – информация о кодировке при ответе
Headers – набор заголовков
OutputStream – поток, в который будет записан ответ (например, текст Html, XML или
байтовый массив изображения)
RedirectLocation – свойство отвечает за HTTP-заголовок Location и позволяет перенаправить
вызов
StatusCode – код статсуса при возврате клиенту, например: 200 (OK), 404 (ресурс не
найден)
StatusDescription – описание статуса при возврате клиенту
Следующий код метода ОбработатьЗапрос преобразует сформированную строку РезультатHTML
с HTML-кодом в набор байт и записывает этот набор байт в выходной поток, который
будет возвращен клиенту. Ссылка на выходной поток получена через параметр метода.
ответСервера = context.Response;
массивБайт = net.GetStatic("System.Text.Encoding", "UTF8").GetBytes(РезультатHTML);
ответСервера.ContentLength64 = массивБайт.Length;
выходнойПоток = ответСервера.OutputStream;
выходнойПоток.Write(массивБайт, 0, массивБайт.Length);
выходнойПоток.Close();
Основой обработки является класс HttpServer, который создает объект HttpListener
и нужное число потоков для обработки. При вызове метода Start происходит запуск
всех потоков-обработчиков и отдельного потока для работы HttpListener. Благодаря
этому, можно продолжать работать с 1С во время работы http-сервера. При поступлении
запроса HttpListener помещает запрос в очередь, где каждый запрос последовательно
обрабатывает первый свободный поток. При обработке потока срабатывает цепочка вызовов:
событие HttpServer.ProcessRequest, обработчик события Helper. HttpServer_ProcessRequest,
1С-функция Форма. ОбработатьЗапрос. Код C# класса HttpServer при запуске 1C из макета
ИсходныйКод компилируется «на лету».
_listener.AuthenticationSchemes = authenticationScheme;
_listener.Prefixes.Add(String.Format(@"http://+:{0}/", port));
_listener.Start();
_listenerThread.Start();
for (int i = 0; i < _workers.Length; i++)
{
_workers[i] = new Thread(Worker);
_workers[i].Start();
}
Начало получения запросов происходит в методе HandleRequests до тех пор, пока обработка
не прекратится пользователем. При поступлении запроса, запрос передается в метод
ContextReady и работа процесса прослушивания продолжается.
private void HandleRequests()
{
while (_listener.IsListening)
{
var context = _listener.BeginGetContext(ContextReady, null);
if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
return;
}
}
private void Worker()
{
WaitHandle[] wait = new[] { _ready, _stop };
while (0 == WaitHandle.WaitAny(wait))
{
HttpListenerContext context;
lock (_queue)
{
if (_queue.Count > 0)
context = _queue.Dequeue();
else
{
_ready.Reset();
continue;
}
}
try { ProcessRequest(context); }
catch (Exception e) { Console.Error.WriteLine(e); }
}
}
Заключение
Многопоточный сервер, описанный в статье, по простоте настройки и функциональности
превосходит традиционные методы доступа извне к информационным базам 1С: Предприятие
8.х. Это простой по запуску метод, позволяющий одновременно обрабатывать несколько
запросов. При этом приложение 1С не блокируется, и пользователь может продолжать
работу после запуска сервера.
Основное преимущество предложенного подхода – полная подконтрольность программисту
процесса от получения запроса до формирования ответа. Например, на этапе получения
запроса может быть выполнен парсинг URL, получена информация о том, как себя идентифицировал
пользователь, а также полная информация о клиенте (поддерживаемые языки, записанные
куки, заголовки, метод доступа). Ответ же можно вернуть практически любой, начиная
от ошибки 404 Not found, заканчивая разными графическими форматами, форматами Word,
Excel и популярными форматами на основе XML (JSON, HTML, RSS).
Пример, приложенный к статье, спроектирован так, что его функциональность можно
легко расширить. Например, для организации кеша применить System.Web.Caching.Cache
класс из .Net framework. А при парсинге URL попробовать поработать с классом RouteCollection
из Asp.Net MVC. При создании RSS-ленты вам поможет класс System.ServiceModel.Syndication
.SyndicationFeed. А при Json-сериализации обратите внимание на класс System.Runtime.Serialization.Json.DataContractJsonSerializer.
Конкретно для предложенного подхода на данный момент не выявлено явных недостатков.
Есть вероятность того, что 1С: Предприятие в силу своих ограничений не сможет обеспечить
на своей стороне должного распараллеливания и значительного увеличения производительности.
Тем не менее опыты проведенные ранее для 1С: Предприятие 8.2 показали, что в схожем
применении 1С достигается увеличение производительности, и 1С работает при этом
стабильно.
Необходимо обратить внимание разработчиков еще на несколько моментов. Любая публикация
информации в Интернете связана с риском взлома вне зависимости от способа публикации.
Но в предложенном способе благодаря гибкости есть больше возможностей противостоять
угрозам извне. Например, в запросе теперь доступен IP-адрес клиента, который можно
блокировать по каким-то правилам (осуществляя поиск в черном списке локально или
в специализированных сервисах). Параметры запроса доступны в виде строк и их можно
анализировать на стороне 1С или .Net framework и блокировать опасное содержимое.
Кроме этого есть несколько специализированных .Net-библиотек, доступных для решения
данной проблемы, которые можно привлечь в 1С, например AntiXSS.
Второй момент заключается в том, что известные методы, в том числе и этот, предоставления
услуг со стороны 1С не предназначены для массовых обращений и будут всегда уступать
с этой точки зрения профессиональным серверам, например, IIS.
Примеры к статье:
HttpServer82.epf (11.50 kb)
TestHttpServer82.epf (8.30 kb)
Вопросы для собеседования
Servlets, JSP, JSTL
- Что такое «сервлет»?
- В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?
- Какова структура веб-проекта?
- Что такое «контейнер сервлетов»?
- Зачем нужны сервера приложений, если есть контейнеры сервлетов?
- Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?
- Что такое «дескриптор развертывания»?
- Какие действия необходимо проделать при создании сервлетов?
- В каком случае требуется переопределять метод
service()
? - Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?
- Почему необходимо переопределить только
init()
метод без аргументов? - Какие наиболее распространенные задачи выполняются в контейнере сервлетов?
- Что вы знаете о сервлетных фильтрах?
- Зачем в сервлетах используются различные listener?
- Когда стоит использовать фильтры сервлетов, а когда слушателей?
- Как реализовать запуск сервлета одновременно с запуском приложения?
- Как обработать в приложении исключения, выброшенные другим сервлетом?
- Что представляет собой
ServletConfig
? - Что представляет собой
ServletContext
? - В чем отличия
ServletContext
иServletConfig
? - Для чего нужен интерфейс
ServletResponse
? - Для чего нужен интерфейс
ServletRequest
? - Что такое
Request Dispatcher
? - Как из одного сервлета вызвать другой сервлет?
- Чем отличается
sendRedirect()
отforward()
? - Для чего используются атрибуты сервлетов и как происходит работа с ними?
- Каким образом можно допустить в сервлете deadlock?
- Как получить реальное расположение сервлета на сервере?
- Как получить информацию о сервере из сервлета?
- Как получить IP адрес клиента на сервере?
- Какие классы-обертки для сервлетов вы знаете?
- В чем отличия
GenericServlet
иHttpServlet
? - Почему
HttpServlet
класс объявлен как абстрактный? - Какие основные методы присутствуют в классе
HttpServlet
? - Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
- Какой метод HTTP не является неизменяемым?
- Какие есть методы отправки данных с клиента на сервер?
- В чем разница между методами
GET
иPOST
? - В чем разница между
PrintWriter
иServletOutputStream
? - Можно ли одновременно использовать в сервлете
PrintWriter
иServletOutputStream
? - Расскажите об интерфейсе
SingleThreadModel
. - Что означает URL encoding? Как это осуществить в Java?
- Какие различные методы управления сессией в сервлетах вы знаете?
- Что такое cookies?
- Какие методы для работы с cookies предусмотрены в сервлетах?
- Что такое URL Rewriting?
- Зачем нужны и чем отличаются методы
encodeURL()
иencodeRedirectURL()
? - Что такое «сессия»?
- Как уведомить объект в сессии, что сессия недействительна или закончилась?
- Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?
- Как мы можем обеспечить transport layer security для нашего веб приложения?
- Как организовать подключение к базе данных, обеспечить журналирование в сервлете?
- Какие основные особенности появились в спецификации Servlet 3?
- Какие способы аутентификации доступны сервлету?
- Что такое Java Server Pages (JSP)?
- Зачем нужен JSP?
- Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
- Расскажите об этапах (фазах) жизненного цикла JSP.
- Расскажите о методах жизненного цикла JSP.
- Какие методы жизненного цикла JSP могут быть переопределены?
- Как можно предотвратить прямой доступ к JSP странице из браузера?
- Какая разница между динамическим и статическим содержимым JSP?
- Как закомментировать код в JSP?
- Какие существуют основные типы тегов JSP?
- Что вы знаете о действиях JSP (Action tag и JSP Action Elements).
- Взаимодействие JSP — сервлет — JSP.
- Какие области видимости переменных существуют в JSP?
- Какие неявные, внутренние объекты и методы есть на JSP странице?
- Какие неявные объекты не доступны в обычной JSP странице?
- Что вы знаете о
PageContext
и какие преимущества его использования? - Как сконфигурировать параметры инициализации для JSP?
- Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?
- Можно ли определить класс внутри JSP страницы?
- Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?
- Какие типы EL операторов вы знаете?
- Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.
- Как отключить возможность использования EL в JSP?
- Как узнать тип HTTP метода используя JSP EL?
- Что такое JSTL (JSP Standard tag library)?
- Из каких групп тегов состоит библиотека JSTL?
- Какая разница между
<c:set>
и<jsp:useBean>
? - Чем отличается
<c:import>
от<jsp:include>
и директивы<%@include %>
? - Как можно расширить функциональность JSP?
- Что вы знаете о написании пользовательских JSP тегов?
- Приведите пример использования собственных тегов.
- Как сделать перенос строки в HTML средствами JSP?
- Почему не нужно конфигурировать стандартные JSP теги в
web.xml
? - Как можно обработать ошибки JSP страниц?
- Как происходит обработка ошибок с помощью JSTL?
- Как конфигурируется JSP в дескрипторе развертывания.
- Можно ли использовать Javascript на JSP странице?
- Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?
- Какая разница между
JSPWriter
и сервлетнымPrintWriter
? - Опишите общие практические принципы работы с JSP.
Что такое «сервлет»?
Сервлет является интерфейсом, реализация которого расширяет функциональные возможности сервера. Сервлет взаимодействует с клиентами посредством принципа запрос-ответ. Хотя сервлеты могут обслуживать любые запросы, они обычно используются для расширения веб-серверов.
Большинство необходимых для создания сервлетов классов и интерфейсов содержатся в пакетах javax.servlet
и javax.servlet.http
.
Основные методы сервлета:
public void init(ServletConfig config) throws ServletException
запускается сразу после загрузки сервлета в память;public ServletConfig getServletConfig()
возвращает ссылку на объект, который предоставляет доступ к информации о конфигурации сервлета;public String getServletInfo()
возвращает строку, содержащую информацию о сервлете, например: автор и версия сервлета;public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException
вызывается для обработки каждого запроса;public void destroy()
выполняется перед выгрузкой сервлета из памяти.
Текущая спецификация — Servlet 3.1 описана в JSR-340 и принята в 2013 году.
к оглавлению
В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?
- Сервлеты предоставляют лучшую производительность обработки запросов и более эффективное использование памяти за счет использования преимущество многопоточности (на каждый запрос создается новая нить, что быстрее выделения памяти под новый объект для каждого запроса, как это происходит в CGI).
- Сервлеты, как платформа и система являются независимыми. Таким образом веб-приложение, написанное с использованием сервлетов может быть запущена в любом контейнере сервлетов, реализующим этот стандарт и в любой операционной системе.
- Использование сервлетов повышает надежность программы, т.к. контейнер сервлетов самостоятельно заботится о жизненном цикле сервлетов (а значит и за утечками памяти), безопасности и сборщике мусора.
- Сервлеты относительно легки в изучении и поддержке, таким образом разработчику необходимо заботиться только о бизнес-логике приложения, а не внутренней реализации веб-технологий.
к оглавлению
Какова структура веб-проекта?
src/main/java
Исходники приложения/библиотеки
src/main/resources
Ресурсные файлы приложения/библиотеки
src/main/filters
Файлы сервлетных фильтров
src/main/webapp
Исходники веб-приложения
src/test/java
Исходники тестов
src/test/resources
Ресурсные файлы тестов
src/test/filters
Тесты сервлетных фильтров
src/it
Интеграционные тесты
src/assembly
Описание сборки
src/site
Сайт
LICENSE.txt
Лицензия проекта
NOTICE.txt
Замечания и определения библиотек зависимостей.
README.txt
Описание проекта
к оглавлению
Что такое «контейнер сервлетов»?
Контейнер сервлетов — программа, представляющая собой сервер, который занимается системной поддержкой сервлетов и обеспечивает их жизненный цикл в соответствии с правилами, определёнными в спецификациях. Может работать как полноценный самостоятельный веб-сервер, быть поставщиком страниц для другого веб-сервера, или интегрироваться в Java EE сервер приложений.
Контейнер сервлетов обеспечивает обмен данными между сервлетом и клиентами, берёт на себя выполнение таких функций, как создание программной среды для функционирующего сервлета, идентификацию и авторизацию клиентов, организацию сессии для каждого из них.
Наиболее известные реализации контейнеров сервлетов:
- Apache Tomcat
- Jetty
- JBoss
- WildFly
- GlassFish
- IBM WebSphere
- Oracle Weblogic
к оглавлению
Зачем нужны сервера приложений, если есть контейнеры сервлетов?
- Пулы соединений с БД
- Возможность периодического тестирования доступности СУБД и обновления соединения в случае восстановления после сбоев
- Замена прав доступа при подключении
- Балансировка нагрузки между несколькими СУБД, определение доступность или недоступность того или иного узла
- Защита пула соединений от некорректного кода в приложении, которое по недосмотру не возвращает соединения, просто отбирая его после какого-то таймаута.
- JMS
- Доступность сервера очередей сообщений «из-коробки».
- Возможность кластеризации очередей, т.е. доступность построения распределенных очередей, расположенных сразу на нескольких серверах, что существенно увеличивает масштабируемость и доступность приложения
- Возможность миграции очередей — в случае падения одного из серверов, его очереди автоматически перемещаются на другой, сохраняя необработанные сообщения.
- В некоторых серверах приложений поддерживается Unit-of-Order — гарантированный порядок обработки сообщений, удовлетворяющих некоторым критериям.
- JTA Встроенная поддержка распределенных транзакций для обеспечения согласованности данных в разные СУБД или очереди.
- Безопасность
- Наличие множества провайдеров безопасности и аутентификации:
- во встроенном или внешнем LDAP-сервере
- в базе данных
- в различных Internet-directory (специализированных приложениях для управления правами доступа)
- Доступность Single-Sign-On (возможности разделения пользовательской сессии между приложениями) посредством Security Assertion Markup Language (SAML) 1/2 или Simple and Protected Negotiate (SPNEGO) и Kerberos: один из серверов выступает в роли базы для хранения пользователей, все другие сервера при аутентификации пользователя обращаются к этой базе.
- Возможность авторизации посредством протокола eXtensible Access Control Markup Language (XACML), позволяющего описывать довольно сложные политики (например, приложение доступно пользователю только в рабочее время).
- Кластеризация всего вышеперечисленного
- Наличие множества провайдеров безопасности и аутентификации:
- Масштабируемость и высокая доступность Для контейнера сервлетов обычно так же возможно настроить кластеризацию, но она будет довольно примитивной, так как в случае его использования имеются следующие ограничения:
- Сложность передачи пользовательской сессии из одного центра обработки данных (ЦоД) в другой через Интернет
- Отсутствие возможности эффективно настроить репликации сессий на большом (состоящем из 40-50 экземпляров серверов) кластере
- Невозможность обеспечения миграции экземпляров приложения на другой сервер
- Недоступность механизмов автоматического мониторинга и реакции на ошибки
- Управляемость
- Присутствие единого центра управления, т.н. AdminServer и аналога NodeManager’а, обеспечивающего
- Возможность одновременного запуска нескольких экземпляров сервера
- Просмотр состояния запущенных экземпляров сервера, обработчиков той или иной очереди, на том или ином сервере, количества соединений с той или иной БД
- Присутствие единого центра управления, т.н. AdminServer и аналога NodeManager’а, обеспечивающего
- Административный канал и развертывание в промышленном режиме Некоторые сервера приложений позволяют включить так называемый «административный канал» — отдельный порт, запросы по которому имеют приоритет.
- Просмотр состояния (выполняющихся транзакций, потоков, очередей) в случае недоступности («зависания») сервера
- Обновление приложений «на-лету», без простоя:
- добавление на сервер новой версии приложения в «закрытом» режиме, пока пользователи продолжают работать со предыдущей
- тестирование корректности развертывания новой версии
- «скрытый» перевод на использование новой версии всех пользователей
к оглавлению
Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?
Контейнер сервлетов управляет четырьмя фазами жизненного цикла сервлета:
- Загрузка класса сервлета — когда контейнер получает запрос для сервлета, то происходит загрузка класса сервлета в память и вызов его конструктора без параметров.
- Инициализация класса сервлета — после того как класс загружен контейнер инициализирует объект
ServletConfig
для этого сервлета и внедряет его черезinit()
метод. Это и есть место где сервлет класс преобразуется из обычного класса в сервлет. - Обработка запросов — после инициализации сервлет готов к обработке запросов. Для каждого запроса клиента сервлет контейнер порождает новый поток и вызывает метод
service()
путем передачи ссылки на объекты ответа и запроса. - Удаление — когда контейнер останавливается или останавливается приложение, то контейнер сервлетов уничтожает классы сервлетов путем вызова
destroy()
метода.
Таким образом, сервлет создаётся при первом обращении к нему и живёт на протяжении всего времени работы приложения (в отличии от объектов классов, которые уничтожаются сборщиком мусора после того, как они уже не используются) и весь жизненный цикл сервлета можно описать как последовательность вызова методов:
public void init(ServletConfig config)
– используется контейнером для инициализации сервлета. Вызывается один раз за время жизни сервлета.public void service(ServletRequest request, ServletResponse response)
– вызывается для каждого запроса. Метод не может быть вызван раньше выполненияinit()
метода.public void destroy()
– вызывается для уничтожения сервлета (один раз за время жизни сервлета).
к оглавлению
Что такое «дескриптор развертывания»?
Дескриптор развертывания — это конфигурационный файл артефакта, который будет развернут в контейнере сервлетов. В спецификации Java Platform, Enterprise Edition дескриптор развертывания описывает то, как компонент, модуль или приложение (такое, как веб-приложение или приложение предприятия) должно быть развернуто.
Этот конфигурационный файл указывает параметры развертывания для модуля или приложения с определенными настройками, параметры безопасности и описывает конкретные требования к конфигурации. Для синтаксиса файлов дескриптора развертывания используется язык XML.
<?xml version="1.0" encoding="UTF-8" ?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <display-name>Display name.</display-name> <description>Description text.</description> <servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>configuration</param-name> <param-value>default</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ExampleServlet</servlet-name> <url-pattern>/example</url-pattern> </servlet-mapping> <servlet> <servlet-name>ExampleJSP</servlet-name> <jsp-file>/sample/Example.jsp</jsp-file> </servlet> <context-param> <param-name>myParam</param-name> <param-value>the value</param-value> </context-param> </web-app>
Для веб-приложений дескриптор развертывания должен называться web.xml
и находиться в директории WEB-INF
, в корне веб-приложения. Этот файл является стандартным дескриптором развертывания, определенным в спецификации. Также есть и другие типы дескрипторов, такие, как файл дескриптора развертывания sun-web.xml
, содержащий специфичные для Sun GlassFish Enterprise Server данные для развертывания именно для этого сервера приложений или файл application.xml
в директории META-INF
для приложений J2EE.
к оглавлению
Какие действия необходимо проделать при создании сервлетов?
Чтобы создать сервлет ExampleServlet
, необходимо описать его в дескрипторе развёртывания:
<servlet-mapping> <servlet-name>ExampleServlet</servlet-name> <url-pattern>/example</url-pattern> </servlet-mapping> <servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>default</param-value> </init-param> </servlet>
Затем создать класс xyz.company.ExampleServlet
путём наследования от HttpServlet
и реализовать логику его работы в методе service()
или методах doGet()
/doPost()
.
к оглавлению
В каком случае требуется переопределять метод service()
?
Метод service()
переопределяется, когда необходимо, чтобы сервлет обрабатывал все запросы (и GET
, и POST
) в одном методе.
Когда контейнер сервлетов получает запрос клиента, то происходит вызов метода service()
, который в зависимости от поступившего запроса вызывает или метод doGet()
или метод doPost()
.
к оглавлению
Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?
Большого смысла определять для сервлета конструктор нет, т.к. инициализировать данные лучше не в конструкторе, а переопределив метод init()
, в котором имеется возможность доступа к параметрам инициализации сервлета через использование объекта ServletConfig
.
к оглавлению
Почему необходимо переопределить только init()
метод без аргументов?
Метод init()
переопределяется, если необходимо инициализировать какие-то данные до того как сервлет начнет обрабатывать запросы.
При переопределении метода init(ServletConfig config)
, первым должен быть вызван метод super(config)
, который обеспечит вызов метода init(ServletConfig config)
суперкласса. GenericServlet
предоставляет другой метод init()
без параметров, который будет вызываться в конце метода init(ServletConfig config)
.
Необходимо использовать переопределенный метод init()
без параметров для инициализации данных во избежание каких-либо проблем, например ошибку, когда вызов super()
не указан в переопределенном init(ServletConfig config)
.
к оглавлению
Какие наиболее распространенные задачи выполняются в контейнере сервлетов?
- Поддержка обмена данными. Контейнер сервлетов предоставляет легкий способ обмена данными между веб клиентом (браузером) и сервлетом. Благодаря контейнеру нет необходимости создавать слушателя сокета на сервере для отслеживания запросов от клиента, а также разбирать запрос и генерировать ответ. Все эти важные и комплексные задачи решаются с помощью контейнера и разработчик может сосредоточиться на бизнес логике приложения.
- Управление жизненным циклом сервлетов и ресурсов. Начиная от загрузки сервлета в память, инициализации, внедрения методов и заканчивая уничтожением сервлета. Контейнер так же предоставляет дополнительные утилиты, например JNDI, для управления пулом ресурсов.
- Поддержка многопоточности. Контейнер самостоятельно создает новую нить для каждого запроса и предоставляет ей запрос и ответ для обработки. Таким образом сервлет не инициализируется заново для каждого запроса и тем самым сохраняет память и уменьшает время до обработки запроса.
- Поддержка JSP. JSP классы не похожи на стандартные классы джавы, но контейнер сервлетов преобразует каждую JSP в сервлет и далее управляется контейнером как обычным сервлетом.
- Различные задачи. Контейнер сервлетов управляет пулом ресурсов, памятью приложения, сборщиком мусора. Предоставляются возможности настройки безопасности и многое другое.
к оглавлению
Что вы знаете о сервлетных фильтрах?
Сервлетный фильтр — это Java-код, пригодный для повторного использования и позволяющий преобразовать содержание HTTP-запросов, HTTP-ответов и информацию, содержащуюся в заголовках HTML. Сервлетный фильтр занимается предварительной обработкой запроса, прежде чем тот попадает в сервлет, и/или последующей обработкой ответа, исходящего из сервлета.
Сервлетные фильтры могут:
- перехватывать инициацию сервлета прежде, чем сервлет будет инициирован;
- определить содержание запроса прежде, чем сервлет будет инициирован;
- модифицировать заголовки и данные запроса, в которые упаковывается поступающий запрос;
- модифицировать заголовки и данные ответа, в которые упаковывается получаемый ответ;
- перехватывать инициацию сервлета после обращения к сервлету.
Сервлетный фильтр может быть конфигурирован так, что он будет работать с одним сервлетом или группой сервлетов. Основой для формирования фильтров служит интерфейс javax.servlet.Filter
, который реализует три метода:
void init(FilterConfig config) throws ServletException
;void destroy()
;void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
;
Метод init()
вызывается прежде, чем фильтр начинает работать,и настраивает конфигурационный объект фильтра. Метод doFilter()
выполняет непосредственно работу фильтра. Таким образом, сервер вызывает init()
один раз, чтобы запустить фильтр в работу, а затем вызывает doFilter()
столько раз, сколько запросов будет сделано непосредственно к данному фильтру. После того, как фильтр заканчивает свою работу, вызывается метод destroy()
.
Интерфейс FilterConfig
содержит метод для получения имени фильтра, его параметров инициации и контекста активного в данный момент сервлета. С помощью своего метода doFilter()
каждый фильтр получает текущий запрос request
и ответ response
, а также FilterChain
, содержащий список фильтров, предназначенных для обработки. В doFilter()
фильтр может делать с запросом и ответом всё, что ему захочется — собирать данные или упаковывать объекты для придания им нового поведения. Затем фильтр вызывает chain.doFilter()
, чтобы передать управление следующему фильтру. После возвращения этого вызова фильтр может по окончании работы своего метода doFilter()
выполнить дополнительную работу над полученным ответом. К примеру, сохранить регистрационную информацию об этом ответе.
После того, как класс-фильтр откомпилирован, его необходимо установить в контейнер и «приписать» (map) к одному или нескольким сервлетам. Объявление и подключение фильтра отмечается в дескрипторе развёртывания web.xml
внутри элементов <filter>
и <filter-mapping>
. Для подключение фильтра к сервлету необходимо использовать вложенные элементы <filter-name>
и <servlet-name>
.
Объявление класс-фильтра
FilterConnect
с именемFilterName
:
<filter> <filter-name>FilterName</filter-name> <filter-class>FilterConnect</filter-class> <init-param> <!--- фильтр имеет параметр инициализации `active`, которому присваивается значение `true`. --> <param-name>active</param-name> <param-value>true</param-true> </init-param> </filter>
Подключение фильтра
FilterName
к сервлетуServletName
:
<filter-mapping> <filter-name>FilterName</filter-name> <servlet-name>ServletName</servlet-name> </filter-mapping>
Для связи фильтра со страницами HTML или группой сервлетов необходимо использовать тег <url-pattern>
:
Подключение фильтра
FilterName
ко всем вызовам .html страниц
<filter-mapping> <filter-name>FilterName</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping>
Порядок, в котором контейнер строит цепочку фильтров для запроса определяется следующими правилами:
- цепочка, определяемая
<url-pattern>
, выстраивается в том порядке, в котором встречаются соответствующие описания фильтров вweb.xml
; - последовательность сервлетов, определенных с помощью
<servlet-name>
, также выполняется в той последовательности, в какой эти элементы встречаются в дескрипторе развёртыванияweb.xml
.
к оглавлению
Зачем в сервлетах используются различные listener?
Listener (слушатель) работает как триггер, выполняя определённые действия при наступлении какого-либо события в жизненном цикле сервлета.
Слушатели, разделённые по области видимости (scope):
- Request:
ServletRequestListener
используется для того, чтобы поймать момент создания и уничтожения запроса;ServletRequestAttributeListener
используется для прослушивания событий, происходящих с атрибутами запроса.
- Context:
ServletContextListener
позволяет поймать момент, когда контекст инициализируется либо уничтожается;ServletContextAttributeListener
используется для прослушивании событий, происходящих с атрибутами в контексте.
- Session:
HttpSessionListener
позволяет поймать момент создания и уничтожения сессии;HttpSessionAttributeListener
используется при прослушивании событий происходящих с атрибутами в сессии;HttpSessionActivationListener
используется в случае, если происходит миграция сессии между различными JVM в распределённых приложениях;HttpSessionBindingListener
так же используется для прослушивания событий, происходящих с атрибутами в сессии. Разница междуHttpSessionAttributeListener
иHttpSessionBindingListener
слушателями: первый декларируется вweb.xml
; экземпляр класса создается контейнером автоматически в единственном числе и применяется ко всем сессиям; второй: экземпляр класса должен быть создан и закреплён за определённой сессией «вручную», количество экземпляров также регулируется самостоятельно.
Подключение слушателей:
<web-app> ... <listener> <listener-class>xyz.company.ExampleListener</listener-class> </listener> ... </web-app>
HttpSessionBindingListener
подключается в качестве атрибута непосредственно в сессию, т.е., чтобы его подключить необходимо:
- создать экземпляр класса, реализующего этот интерфейс;
- положить созданный экземпляр в сессию при помощи
setAttribute(String, Object)
.
к оглавлению
Когда стоит использовать фильтры сервлетов, а когда слушателей?
Следует использовать фильтры, если необходимо обрабатывать входящие или исходящие данные (например: для аутентификации, преобразования формата, компрессии, шифрования и т.д.), в случае, когда необходимо реагировать на события — лучше применять слушателей.
к оглавлению
Как реализовать запуск сервлета одновременно с запуском приложения?
Контейнер сервлетов обычно загружает сервлет по первому запросу клиента.
Если необходимо загрузить сервлет прямо на старте приложения (например если загрузка сервлета происходит длительное время) следует использовать элемент <load-on-startup>
в дескрипторе или аннотацию @loadOnStartup
в коде сервлета, что будет указывать на необходимость загрузки сервлета при запуске.
Если целочисленное значение этого параметра отрицательно, то сервлет будет загружен при запросе клиента. В противном случае — загрузится на старте приложения, при этом, чем число меньше, тем раньше в очереди на загрузку он окажется.
<servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
к оглавлению
Как обработать в приложении исключения, выброшенные другим сервлетом?
Когда приложение выбрасывет исключение контейнер сервлетов обрабатывает его и создаёт ответ в формате HTML. Это аналогично тому, что происходит при кодах ошибок вроде 404, 403 и т.д.
В дополнении к этому существует возможность написания собственных сервлетов для обработки исключений и ошибок с указанием их в дескрипторе развертывания:
<error-page> <error-code>404</error-code> <location>/AppExceptionHandler</location> </error-page> <error-page> <exception-type>javax.servlet.ServletException</exception-type> <location>/AppExceptionHandler</location> </error-page>
Основная задача таких сервлетов — обработать ошибку/исключение и сформировать понятный ответ пользователю. Например, предоставить ссылку на главную страницу или же описание ошибки.
к оглавлению
Что представляет собой ServletConfig
?
Интерфейс javax.servlet.ServletConfig
используется для передачи сервлету конфигурационной информации. Каждый сервлет имеет свой собственный экземпляр объекта ServletConfig
, создаваемый контейнером сервлетов.
Для установки параметров конфигурации используются параметры init-param
в web.xml
:
<servlet> <servlet-name>ExampleServlet</servlet-name> <servlet-class>xyz.company.ExampleServlet</servlet-class> <init-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </init-param> </servlet>
или аннотации @WebInitParam
:
@WebServlet( urlPatterns = "/example", initParams = { @WebInitParam(name = "exampleParameter", value = "parameterValue") } ) public class ExampleServlet extends HttpServlet { //... }
Для получения ServletConfig
сервлета используется метод getServletConfig()
.
к оглавлению
Что представляет собой ServletContext
?
Уникальный (в рамках веб-приложения) объект ServletContext
реализует интерфейс javax.servlet.ServletContext
и предоставляет сервлетам доступ к параметрам этого веб-приложения. Для предоставления доступа используется элемент <context-param>
в web.xml
:
<web-app> ... <context-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </context-param> ... </web-app>
Объект ServletContext
можно получить с помощью метода getServletContext()
у интерфейса ServletConfig
. Контейнеры сервлетов так же могут предоставлять контекстные объекты, уникальные для группы сервлетов. Каждая из групп будет связана со своим набором URL-путей хоста. В спецификации Servlet 3 ServletContext
был расширен и теперь предоставляет возможности программного добавления слушателей и фильтров в приложение. Так же у этого интерфейса имеется множество полезных методов таких как getServerInfo()
, getMimeType()
, getResourceAsStream()
и т.д.
к оглавлению
В чем отличия ServletContext
и ServletConfig
?
ServletConfig
уникален для сервлета, аServletContext
— для приложения;ServletConfig
используется для предоставления параметров инициализации конкретному сервлету, аServletContext
для предоставления параметров инициализации для всех сервлетов приложения;- для
ServletConfig
возможности модифицировать атрибуты отсутствуют, атрибуты в объектеServletContext
можно изменять.
к оглавлению
Для чего нужен интерфейс ServletResponse
?
Интерфейс ServletResponse
используется для отправки данных клиенту. Все методы данного инструмента служат именно этой цели:
String getCharacterEncoding()
— возвращает MIME тип кодировки (к примеру — UTF8), в которой будет выдаваться информация;void setLocale(Locale locale)
/Locale getLocale()
— указывают на язык используемый в документе;ServletOutputStream getOutputStream()
/PrintWriter getWriter()
— возвращают потоки вывода данных;void setContentLength(int len)
— устанавливает значение поля HTTP заголовка Content-Length;void setContentType(String type)
— устанавливает значение поля HTTP заголовка Content-Type.void reset()
— позволяет сбросить HTTP заголовок к значениям по-умолчанию, если он ещё не был отправлен- и др.
к оглавлению
Для чего нужен интерфейс ServletRequest
?
Интерфейс ServletRequest
используется для получения параметров соединения, запроса, а также заголовков, входящего потока данных и т.д.
к оглавлению
Что такое Request Dispatcher
?
Интерфейс RequestDispatcher
используется для передачи запроса другому ресурсу, при этом существует возможность добавления данных, полученных из этого ресурса к собственному ответу сервлета. Так же этот интерфейс используется для внутренней коммуникации между сервлетами в одном контексте.
В интерфейсе объявлено два метода:
void forward(ServletRequest var1, ServletResponse var2)
— передает запрос из сервлета к другому ресурсу (сервлету, JSP или HTML файлу) на сервере.void include(ServletRequest var1, ServletResponse var2)
— включает контент ресурса (сервлет, JSP или HTML страница) в ответ.
Доступ к интерфейсу можно получить с помощью метода интерфейса ServletContext
— RequestDispatcher getRequestDispatcher(String path)
, где путь начинающийся с /
, интерпретируется относительно текущего корневого пути контекста.
к оглавлению
Как из одного сервлета вызвать другой сервлет?
Для вызова сервлета из того же приложения необходимо использовать механизм внутренней коммуникации сервлетов (inter-servlet communication mechanisms) через вызовы методов RequestDispatcher
:
forward()
— передаёт выполнение запроса в другой сервлет;include()
— предоставляет возможность включить результат работы другого сервлета в возвращаемый ответ.
Если необходимо вызывать сервлет принадлежащий другому приложению, то использовать RequestDispatcher
уже не получится, т.к. он определен только для текущего приложения. Для подобных целей необходимо использовать метод ServletResponse
— sendRedirect()
которому предоставляется полный URL другого сервлета. Для передачи данных между сервлетами можно использовать cookies
.
к оглавлению
Чем отличается sendRedirect()
от forward()
?
forward()
:
- Выполняется на стороне сервера;
- Запрос перенаправляется на другой ресурс в пределах того же сервера;
- Не зависит от протокола клиентского запроса, так как обеспечивается контейнером сервлетов;
- Нельзя применять для внедрения сервлета в другой контекст;
- Клиент не знает о фактически обрабатываемом ресурсе и URL в строке остается прежним;
- Выполняется быстрее метода
sendRedirect()
; - Определён в интерфейсе
RequestDispatcher
.
sendRedirect()
:
- Выполняется на стороне клиента;
- Клиенту возвращается ответ
302 (redirect)
и запрос перенаправляется на другой сервер; - Может использоваться только с клиентами HTTP;
- Разрешается применять для внедрения сервлета в другой контекст;
- URL адрес изменяется на адрес нового ресурса;
- Медленнее
forward()
т.к. требует создания нового запроса; - Определён в интерфейсе
HttpServletResponse
.
к оглавлению
Для чего используются атрибуты сервлетов и как происходит работа с ними?
Атрибуты сервлетов используются для внутренней коммуникации сервлетов.
В веб-приложении существует возможность работы с атрибутами используя методы setAttribute()
, getAttribute()
, removeAttribute()
, getAttributeNames()
, которые предоставлены интерфейсами ServletRequest
, HttpSession
и ServletContext
(для областей видимости request, session, context соответственно).
к оглавлению
Каким образом можно допустить в сервлете deadlock?
Можно получить блокировку, например, допустив циклические вызовы метода doPost()
в методе doGet()
и метода doGet()
в методе doPost()
.
к оглавлению
Как получить реальное расположение сервлета на сервере?
Реальный путь к расположению сервлета на сервере можно получить из объекта ServletContext
:
getServletContext().getRealPath(request.getServletPath())
.
к оглавлению
Как получить информацию о сервере из сервлета?
Информацию о сервере можно получить из объекта ServletContext
:
getServletContext().getServerInfo()
.
к оглавлению
Как получить IP адрес клиента на сервере?
IP адрес клиента можно получить вызвав request.getRemoteAddr()
.
к оглавлению
Какие классы-обертки для сервлетов вы знаете?
Собственные обработчики ServletRequest
и ServletResponse
можно реализовать, добавив новые или переопределив существующие методы у классов-обёрток ServletRequestWrapper
(HttpServletRequestWrapper
) и ServletResponseWrapper
(HttpServletRequestWrapper
).
к оглавлению
В чем отличия GenericServlet
и HttpServlet
?
Абстрактный класс GenericServlet
— независимая от используемого протокола реализация интерфейса Servlet
, а абстрактный класс HttpServlet
в свою очередь расширяет GenericServlet
для протокола HTTP..
к оглавлению
Почему HttpServlet
класс объявлен как абстрактный?
Класс HTTPServlet
предоставляет лишь общую реализацию сервлета для HTTP протокола. Реализация ключевых методов doGet()
и doPost()
, содержащих основную бизнес-логику перекладывается на разработчика и по умолчанию возвращает HTTP 405 Method Not Implemented error
.
к оглавлению
Какие основные методы присутствуют в классе HttpServlet
?
doGet()
— для обработки HTTP запросовGET
;doPost()
— для обработки HTTP запросовPOST
;doPut()
— для обработки HTTP запросовPUT
;doDelete()
— для обработки HTTP запросовDELETE
;doHead()
— для обработки HTTP запросовHEAD
;doOptions()
— для обработки HTTP запросовOPTIONS
;doTrace()
— для обработки HTTP запросовTRACE
.
к оглавлению
Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
Методы init()
и destroy()
вызываются один раз за жизненный цикл сервлета — поэтому по поводу них беспокоиться не стоит.
Методы doGet()
, doPost()
, service()
вызываются на каждый запрос клиента и т.к. сервлеты используют многопоточность, то здесь задумываться о потокобезопасной работе обязательно. При этом правила использования многопоточности остаются теми же: локальные переменные этих методов будут созданы отдельно для каждого потока, а при использовании глобальных разделяемых ресурсов необходимо использовать синхронизацию или другие приёмы многопоточного программирования.
к оглавлению
Какой метод HTTP не является неизменяемым?
HTTP метод называется неизменяемым, если он на один и тот же запрос всегда возвращает одинаковый результат. HTTP методы GET
, PUT
, DELETE
, HEAD
и OPTIONS
являются неизменяемыми, поэтому необходимо реализовывать приложение так, чтобы эти методы возвращали одинаковый результат постоянно. К изменяемым методам относится метод POST
, который и используется для реализации чего-либо, что изменяется при каждом запросе.
К примеру, для доступа к статической HTML странице используется метод GET
, т.к. он всегда возвращает одинаковый результат. При необходимости сохранять какую-либо информацию, например в базе данных, нужно использовать POST
метод.
к оглавлению
Какие есть методы отправки данных с клиента на сервер?
GET
— используется для запроса содержимого указанного ресурса, изображения или гипертекстового документа. Вместе с запросом могут передаваться дополнительные параметры как часть URI, значения могут выбираться из полей формы или передаваться непосредственно через URL. При этом запросы кэшируются и имеют ограничения на размер. Этот метод является основным методом взаимодействия браузера клиента и веб-сервера.POST
— используется для передачи пользовательских данных в содержимом HTTP-запроса на сервер. Пользовательские данные упакованы в тело запроса согласно полю заголовка Content-Type и/или включены в URI запроса. При использовании метода POST под URI подразумевается ресурс, который будет обрабатывать запрос.
к оглавлению
В чем разница между методами GET
и POST
?
GET
передает данные серверу используя URL, тогда какPOST
передает данные, используя тело HTTP запроса. Длина URL ограничена 1024 символами, это и будет верхним ограничением для данных, которые можно отослать черезGET
.POST
может отправлять гораздо большие объемы данных. Лимит устанавливается web-server и составляет обычно около 2 Mb.- Передача данных методом
POST
более безопасна, чем методомGET
, так как секретные данные (например пароль) не отображаются напрямую в web-клиенте пользователя, в отличии от URL, который виден почти всегда. Иногда это преимущество превращается в недостаток — вы не сможете послать данные за кого-то другого. GET
метод является неизменяемым, тогда какPOST
— изменяемый.
к оглавлению
В чем разница между PrintWriter
и ServletOutputStream
?
PrintWriter
— класс для работы с символьным потоком, экземпляр которого можно получить через метод ServletResponse
getWriter()
;
ServletOutputStream
— класс для работы байтовым потоком. Для получения его экземпляра используется метод ServletResponse
getOutputStream()
.
к оглавлению
Можно ли одновременно использовать в сервлете PrintWriter
и ServletOutputStream
?
Так сделать не получится, т.к. при попытке одновременного вызова getWriter()
и getOutputStream()
будет выброшено исключение java.lang.IllegalStateException
с сообщением, что уже был вызван другой метод.
к оглавлению
Расскажите об интерфейсе SingleThreadModel
.
Интерфейс SingleThreadModel
является маркерным — в нем не объявлен ни один метод, однако, если сервлет реализует этот интерфейс, то метод service()
этого сервлета гарантированно не будет одновременно выполняться в двух потоках. Контейнер сервлетов либо синхронизирует обращения к единственному экземпляру, либо обеспечивает поддержку пула экземпляров и перенаправление запроса свободному сервлету.
Другими словами, контейнер гарантирует отсутствие конфликтов при одновременном обращении к переменным или методам экземпляра сервлета. Однако существуют также и другие разделяемые ресурсы, которые даже при использовании этого интерфейса, остаются всё так же доступны обработчикам запросов в других потоках. Т.о. пользы от использования этого интерфейса немного и в спецификации Servlet 2.4 он был объявлен deprecated
.
к оглавлению
Что означает URL encoding? Как это осуществить в Java?
URL Encoding — процесс преобразования данных в форму CGI (Common Gateway Interface), не содержащую пробелов и нестандартных символов, которые заменяются в процессе кодирования на специальные escape-символы. В Java для кодирования строки используется метод java.net.URLEncoder.encode(String str, String unicode)
. Обратная операция декодирования возможна через использование метода java.net.URLDecoder.decode(String str, String unicode)
.
Hello мир!
преобразовывается вHello%20%D0%BC%D0%B8%D1%80!
.
к оглавлению
Какие различные методы управления сессией в сервлетах вы знаете?
При посещении клиентом Web-ресурса и выполнении вариантов запросов, контекстная информация о клиенте не хранится. В протоколе HTTP нет возможностей для сохранения и изменения информации о предыдущих посещениях клиента. Сеанс (сессия) – соединение между клиентом и сервером, устанавливаемое на определенное время, за которое клиент может отправить на сервер сколько угодно запросов. Сеанс устанавливается непосредственно между клиентом и Web-сервером. Каждый клиент устанавливает с сервером свой собственный сеанс. Сеансы используются для обеспечения хранения данных во время нескольких запросов Web-страницы или на обработку информации, введенной в пользовательскую форму в результате нескольких HTTP-соединений (например, клиент совершает несколько покупок в интернет-магазине; студент отвечает на несколько тестов в системе дистанционного обучения).
Существует несколько способов обеспечения уникального идентификатора сессии:
- User Authentication – Предоставление учетных данных самим пользователем в момент аутентификации. Переданная таким образом информация в дальнейшем используется для поддержания сеанса. Это метод не будет работать, если пользователь вошёл в систему одновременно из нескольких мест.
- HTML Hidden Field – Присвоение уникального значения скрытому полю HTML страницы, в момент когда пользователь начинает сеанс. Этот метод не может быть использован со ссылками, потому что нуждается в подтверждении формы со скрытым полем каждый раз во время формирования запроса. Кроме того, это не безопасно, т.к. существует возможность простой подмены такого идентификатора.
- URL Rewriting – Добавление идентификатора сеанса как параметра URL. Достаточно утомительная операция, потому что требует постоянного отслеживания этого идентификатора при каждом запросе или ответе.
- Cookies – Использование небольших фрагментов данных, отправленных web-сервером и хранимых на устройстве пользователя. Данный метод не будет работать, если клиент отключает использование cookies.
- Session Management API – Использование специального API для отслеживания сеанса, построенный на основе и на методах, описанных выше и который решает частные проблемы перечисленных способов:
- Чаще всего недостаточно просто отслеживать сессию, необходимо ещё и сохранять какие-либо дополнительные данные о ней, которые могут потребоваться при обработке последующих запросов. Осуществление такого поведения требует много дополнительных усилий.
- Все вышеперечисленные методы не являются универсальными: для каждого из них можно подобрать конкретный сценарий, при котором они не будут работать.
к оглавлению
Что такое cookies?
Сookies («куки») — небольшой фрагмент данных, отправленный web-сервером и хранимый на устройстве пользователя. Всякий раз при попытке открыть страницу сайта, web-клиент пересылает соответствующие этому сайту cookies web-серверу в составе HTTP-запроса. Применяется для сохранения данных на стороне пользователя и на практике обычно используется для:
- аутентификации пользователя;
- хранения персональных предпочтений и настроек пользователя;
- отслеживания состояния сеанса доступа пользователя;
- ведения разнообразной статистики.
к оглавлению
Какие методы для работы с cookies предусмотрены в сервлетах?
Servlet API предоставляет поддержку cookies через класс javax.servlet.http.Cookie
:
- Для получения массива cookies из запроса необходимо воспользоваться методом
HttpServletRequest.getCookies()
. Методов для добавления cookies вHttpServletRequest
не предусмотрено. - Для добавления cookie в ответ используется
HttpServletResponse.addCookie(Cookie c)
. Метода получения cookies вHttpServletResponse
отсутствует.
к оглавлению
Что такое URL Rewriting?
URL Rewriting — специальная перезапись (перекодирование) оригинального URL. Данный механизм может использоваться для управления сессией в сервлетах, когда cookies отключены.
к оглавлению
Зачем нужны и чем отличаются методы encodeURL()
и encodeRedirectURL()
?
HttpServletResponse.encodeURL()
предоставляет способ преобразования URL в HTML гиперссылку с преобразованием спецсимволов и пробелов, а так же добавления session id к URL. Такое поведение аналогично java.net.URLEncoder.encode()
, но с добавлением дополнительного параметра jsessionid
в конец URL.
Метод HttpServletResponse.encodeRedirectURL()
преобразует URL для последующего использования в методе sendRedirect()
.
Таким образом для HTML гиперссылок при URL rewriting необходимо использовать encodeURL()
, а для URL при перенаправлении — encodeRedirectUrl()
.
к оглавлению
Что такое «сессия»?
Сессия — это сеанс связи между клиентом и сервером, устанавливаемый на определенное время. Сеанс устанавливается непосредственно между клиентом и веб-сервером в момент получения первого запроса к веб-приложению. Каждый клиент устанавливает с сервером свой собственный сеанс, который сохраняется до окончания работы с приложением.
к оглавлению
Как уведомить объект в сессии, что сессия недействительна или закончилась?
Чтобы быть уверенным в том, что объект будет оповещён о прекращении сессии, нужно реализовать интерфейс javax.servlet.http.HttpSessionBindingListener
. Два метода этого интерфейса: valueBound()
и valueUnbound()
используются при добавлении объекта в качестве атрибута к сессии и при уничтожении сессии соответственно.
к оглавлению
Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?
Сервлет фильтры используются для перехвата всех запросов между контейнером сервлетов и сервлетом. Поэтому логично использовать соответствующий фильтр для проверки необходимой информации (например валидности сессии) в запросе.
к оглавлению
Как мы можем обеспечить transport layer security для нашего веб приложения?
Для обеспечения transport layer security необходимо настроить поддержку SSL сервлет контейнера. Как это сделать зависит от конкретной реализации сервлет-контейнера.
к оглавлению
Как организовать подключение к базе данных, обеспечить журналирование в сервлете?
При работе с большим количеством подключений к базе данных рекомендуется инициализировать их в servlet context listener, а также установить в качестве атрибута контекста для возможности использования другими сервлетами.
Журналирование подключается к сервлету стандартным для логгера способом (например для log4j это может быть property-файл или XML-конфигурация) , а далее эта информация используется при настройке соответствующего context listener.
к оглавлению
Какие основные особенности появились в спецификации Servlet 3?
- Servlet Annotations. До Servlet 3 вся конфигурация содержалась в
web.xml
, что приводило к ошибкам и неудобству при работе с большим количестве сервлетов. Примеры аннотаций:@WebServlet
,@WebInitParam
,@WebFilter
,@WebListener
. - Web Fragments. Одностраничное веб приложение может содержать множество модулей: все модули прописываются в
fragment.xml
в папкеMETA-INF
. Это позволяет разделять веб приложение на отдельные модули, собранные как .jar-файлы в отдельнойlib
директории. - Динамическое добавление веб компонентов. Появилась возможность программно добавлять фильтры и слушатели, используя
ServletContext
объект. Для этого применяются методыaddServlet()
,addFilter()
,addListener()
. Используя это нововведение стало доступным построение динамической системы, в которой необходимый объект будет создан и вызван только по необходимости. - Асинхронное выполнение. Поддержка асинхронной обработки позволяет передать выполнение запроса в другой поток без удержания всего сервера занятым.
к оглавлению
Какие способы аутентификации доступны сервлету?
Спецификация сервлетов определяет четыре типа проверки подлинности:
- HTTP Basic Authentication —
BASIC
. При доступе к закрытым ресурсам появится окно, которое попросит ввести данные для аутентификации. - Form Based Login —
FORM
. Используется собственная html форма: - HTTP Digest Authentication —
DIGEST
. Цифровая аутентификация с шифрованием. - HTTPS Authentication —
CLIENT-CERT
. Аутентификация с помощью клиентского сертификата.
<login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.html</form-login-page> <form-error-page>/error.html</form-error-page> </form-login-config> </login-config>
к оглавлению
Что такое Java Server Pages (JSP)?
JSP (JavaServer Pages) — платформонезависимая переносимая и легко расширяемая технология разработки веб-приложений, позволяющая веб-разработчикам создавать содержимое, которое имеет как статические, так и динамические компоненты. Страница JSP содержит текст двух типов: статические исходные данные, которые могут быть оформлены в одном из текстовых форматов HTML, SVG, WML, или XML, и JSP-элементы, которые конструируют динамическое содержимое. Кроме этого могут использоваться библиотеки JSP-тегов, а также EL (Expression Language), для внедрения Java-кода в статичное содержимое JSP-страниц.
Код JSP-страницы транслируется в Java-код сервлета с помощью компилятора JSP-страниц Jasper, и затем компилируется в байт-код JVM.
JSP-страницы загружаются на сервере и управляются Java EE Web Application. Обычно такие страницы упакованы в файловые архивы .war и .ear.
к оглавлению
Зачем нужен JSP?
JSP расширяет технологию сервлетов обеспечивая возможность создания динамических страницы с HTML подобным синтаксисом.
Хотя создание представлений поддерживается и в сервлетах, но большая часть любой веб-страницы является статической, поэтому код сервлета в таком случае получается чересчур перегруженным, замусоренным и поэтому при его написании легко допустить ошибку.
Еще одним преимуществом JSP является горячее развертывание — возможность заменить одну страницу на другую непосредственно в контейнере без необходимости перекомпилировать весь проект или перезапускать сервер.
Однако рекомендуется избегать написания серьёзной бизнес-логики в JSP и использовать страницу только в качестве представления.
к оглавлению
Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
Когда пользователь переходит по ссылке на страницу page.jsp
, он отправляет http-запрос на сервер GET /page.jsp
. Затем, на основе этого запроса и текста самой страницы, сервер генерирует java-класс, компилирует его и выполняет полученный сервлет, формирующий ответ пользователю в виде представления этой страницы, который сервер и перенаправляет обратно пользователю.
к оглавлению
Расскажите об этапах (фазах) жизненного цикла JSP.
Если посмотреть код внутри созданной JSP страницы, то он будет выглядеть как HTML и не будет похож на java класс. Конвертацией JSP страниц в HTML код занимается контейнер, который так же создает и сервлет для использования в веб приложении.
Жизненный цикл JSP состоит из нескольких фаз, которыми руководит JSP контейнер:
- Translation – проверка и парсинг кода JSP страницы для создания кода сервлета.
- Compilation – компиляция исходного кода сервлета.
- Class Loading – загрузка скомпилированного класса в память.
- Instantiation – внедрение конструктора без параметра загруженного класса для инициализации в памяти.
- Initialization – вызов
init()
метода объекта JSP класса и инициализация конфигурации сервлета с первоначальными параметрами, которые указаны в дескрипторе развертывания (web.xml
). После этой фазы JSP способен обрабатывать запросы клиентов. Обычно эти фазы происходят после первого запроса клиента (т.е. ленивая загрузка), но можно настроить загрузку и инициализацию JSP на старте приложения по аналогии с сервлетами. - Request Processing – длительный жизненный цикл обработки запросов клиента JSP страницей. Обработка является многопоточной и аналогична сервлетам — для каждого запроса создается новый поток, объекты
ServletRequest
иServletResponse
, происходит выполнение сервис методов. - Destroy – последняя фаза жизненного цикла JSP, на которой её класс удаляется из памяти. Обычно это происходит при выключении сервера или выгрузке приложения.
к оглавлению
Расскажите о методах жизненного цикла JSP.
Контейнер сервлетов (например, Tomcat, GlassFish) создает из JSP-страницы класс сервлета, наследующего свойства интерфейса javax.servlet.jsp.HttpJspBase
и включающего следующие методы:
jspInit()
— метод объявлен в JSP странице и реализуется с помощью контейнера. Этот метод вызывается один раз в жизненном цикле JSP для того, чтобы инициализировать конфигурационные параметры указанные в дескрипторе развертывания. Этот метод можно переопределить с помощью определения элемента JSP scripting и указания необходимых параметров для инициализации;_jspService()
— метод переопределяется контейнером автоматически и соответствует непосредственно коду JSP, описанному на странице. Этот метод определен в интерфейсеHttpJspPage
, его имя начинается с нижнего подчеркивания и он отличается от других методов жизненного цикла тем, что его невозможно переопределить;jspDestroy()
— метод вызывается контейнером для удаления объекта из памяти (на последней фазе жизненного цикла JSP — Destroy). Метод вызывается только один раз и доступен для переопределения, предоставляя возможность освободить ресурсы, которые были созданы вjspInit()
.
к оглавлению
Какие методы жизненного цикла JSP могут быть переопределены?
Возможно переопределить лишь jspInit()
и jspDestroy()
методы.
к оглавлению
Как можно предотвратить прямой доступ к JSP странице из браузера?
Прямой доступ к директории /WEB-INF/
из веб-приложения отсутствует. Поэтому JSP-страницы можно расположить внутри этой папки и тем самым запретить доступ к странице из браузера. Однако, по аналогии с описанием сервлетов, будет необходимо настроить дескриптор развертывания:
<servlet> <servlet-name>Example</servlet-name> <jsp-file>/WEB-INF/example.jsp</jsp-file> <init-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Example</servlet-name> <url-pattern>/example.jsp</url-pattern> </servlet-mapping>
к оглавлению
Какая разница между динамическим и статическим содержимым JSP?
Статическое содержимое JSP (HTML, код JavaScript, изображения и т.д.) не изменяется в процессе работы веб приложения.
Динамические ресурсы созданы для того, чтобы отображать свое содержимое в зависимости от пользовательских действий. Обычно они представлены в виде выражений EL (Expression Language), библиотек JSP-тегов и пр.
к оглавлению
Как закомментировать код в JSP?
<!—- HTML комментарий; отображается на странице JSP —->
такие комментарии будут видны клиенту при просмотре кода страницы.<%—- JSP комментарий; не отображается на странице JSP —-%>
такие комментарии описываются в созданном сервлете и не посылаются клиенту. Для любых комментариев по коду или отладочной информации необходимо использовать именно такой тип комментариев.
к оглавлению
Какие существуют основные типы тегов JSP?
- Выражение JSP:
<%= expression %>
— выражение, которое будет обработано с перенаправлением результата на вывод; - Скриплет JSP:
<% code %>
— код, добавляемый в методservice()
. - Объявление JSP:
<%! code %>
— код, добавляемый в тело класса сервлета вне методаservice()
. - Директива JSP page:
<%@ page att="value" %>
— директивы для контейнера сервлетов с информацией о параметрах. - Директива JSP include:
<%@ include file="url" %>
— файл в локальной системе, подключаемый при трансляции JSP в сервлет. - Комментарий JSP:
<%-- comment --%>
— комментарий; игнорируется при трансляции JSP страницы в сервлет.
к оглавлению
Что вы знаете о действиях JSP (Action tag и JSP Action Elements).
Action tag и JSP Action Elements предоставляют методы работы с Java Beans, подключения ресурсов, проброса запросов и создания динамических XML элементов. Такие элементы всегда начинаются с записи jsp:
и используются непосредственно внутри страницы JSP без необходимости подключения сторонних библиотек или дополнительных настроек.
Наиболее часто используемыми JSP Action Elements являются:
jsp:include
:<jsp:include page="относительный URL" flush="true"/>
— подключить файл при запросе страницы. Если необходимо, чтобы файл подключался в процессе трансляции страницы, то используется директиваpage
совместно с атрибутомinclude
;jsp:useBean
:<jsp:useBean att=значение*/>
или<jsp:useBean att=значение*>...</jsp:useBean>
— найти или создать Java bean;jsp:setProperty
:<jsp:setProperty att=значение*/>
— установить свойства Java bean, или явно, или указанием на соответствующее значение, передаваемое при запросе;jsp:forward
:<jsp:forward page="относительный URL"/>
— передать запрос другой странице;jsp:plugin
:<jsp:plugin attribute="значение"*>...</jsp:plugin>
— сгенерировать (в зависимости от типа браузера) тэгиOBJECT
илиEMBED
для апплета, использующего технологию Java Plugin.
к оглавлению
Взаимодействие JSP — сервлет — JSP.
«JSP — сервлет — JSP» архитектура построения приложений носит название MVC (Model/View/Controller):
-
Model — классы данных и бизнес-логики;
-
View — страницы JSP;
-
Controller — сервлеты.
к оглавлению
Какие области видимости переменных существуют в JSP?
Область видимости объектов определяется тем контекстом, в который помещается данный объект. В зависимости от той или иной области действия так же определяется время существования объекта.
В JSP предусмотрены следующие области действия переменных (объектов):
request
область действия запроса — объект будет доступен на текущей JSP странице, странице пересылки (при использованииjsp:forward
) или на включаемой странице (при использованииjsp:include
);session
область действия сессии — объект будет помещен в сеанс пользователя, будет доступен на всех JSP страницах и будет существовать пока существует сессия пользователя, или он не будет из нее принудительно удален.application
область действия приложения — объект будет доступен для всех пользователей на всех JSP страницах и будет существовать на протяжении всей работы приложения или пока не будет удален принудительно и контекста приложения.page
область действия страницы — объект будет доступен только на той странице, где он определен. На включаемых (jsp:include
) и переадресуемых (jsp:forward
) страницах данный объект уже не будет доступен.
Таким образом, чтобы объект был доступен всем JSP страницам, необходимо указать область видимости application
или session
, в зависимости от того требуется ли доступ к объекту всем пользователям или только текущему.
Для указания требуемой области действия при определении объекта на JSP странице используется атрибут scope тега jsp:useBean
:
<jsp:useBean id="myBean" class="ru.javacore.MyBean" scope="session"/>
Если не указывать атрибут scope
, то по умолчанию задается область видимости страницы page
к оглавлению
Какие неявные, внутренние объекты и методы есть на JSP странице?
JSP implicit objects (неявные объекты) создаются контейнером при конвертации JSP страницы в код сервлета для помощи разработчикам. Эти объекты можно использовать напрямую в скриптлетах для передачи информации в сервис методы, однако мы не можем использовать неявные объекты в JSP Declaration, т.к. такой код пойдет на уровень класса.
Существует 9 видов неявных объектов, которые можно использовать прямо на JSP странице. Семь из них объявлены как локальные переменные в начале _jspService()
метода, а два оставшихся могут быть использованы как аргументы метода _jspService()
.
out Object
:
<strong>Current Time is</strong>: <% out.print(new Date()); %><br>
request Object
:
<strong>Request User-Agent</strong>: <%=request.getHeader("User-Agent") %><br>
response Object
:
<strong>Response</strong>: <%response.addCookie(new Cookie("Test","Value")); %>
config Object
:
<strong>User init param value</strong>: <%=config.getInitParameter("User") %><br>
application Object
:
<strong>User context param value</strong>: <%=application.getInitParameter("User") %><br>
session Object
:
<strong>User Session ID</strong>: <%=session.getId() %><br>
pageContext Object
:
<% pageContext.setAttribute("Test", "Test Value"); %>
<strong>PageContext attribute</strong>: {Name="Test",Value="<%=pageContext.getAttribute("Test") %>"}<br>
page Object
:
<strong>Generated Servlet Name</strong>: <%=page.getClass().getName() %>
exception Object
:
<strong>Exception occured</strong>: <%=exception %><br>
к оглавлению
Какие неявные объекты не доступны в обычной JSP странице?
Неявный объект исключений JSP недоступен в обычных JSP страницах и используется на страницах ошибок JSP (errorpage) только для того, чтобы перехватить исключение, выброшенное JSP страницей и далее предоставить какую-либо полезную информацию клиенту.
к оглавлению
Что вы знаете о PageContext
и какие преимущества его использования?
Неявный объект JSP — экземпляр класса javax.servlet.jsp.PageContext
предоставляет доступ ко всем пространствам имён, ассоциированным с JSP-страницей, а также к различным её атрибутам.
Остальные неявные объекты добавляются к pageContext
автоматически.
Класс PageContext
это абстрактный класс, а его экземпляр можно получить через вызов метода JspFactory.getPageContext()
, и освободить через вызов метода JspFactory.releasePageContext()
.
PageContext
обладает следующим набором особенностей и возможностей:
- единый API для обслуживания пространств имён различных областей видимости;
- несколько удобных API для доступа к различным
public
-объектам; - механизм получения
JspWriter
для вывода; - механизм обслуживания использования сессии страницей;
- механизм экспонирования («показа») атрибутов директивы
page
среде скриптинга; - механизмы направления или включения текущего запроса в другие компоненты приложения;
- механизм обработки процессов исключений на страницах ошибок errorpage;
к оглавлению
Как сконфигурировать параметры инициализации для JSP?
Параметры инициализации для JSP задаются в web.xml
файле аналогично сервлетам — элементами servlet
и servlet-mapping
. Единственным отличием будет указание местонахождения JSP страницы:
<servlet> <servlet-name>Example</servlet-name> <jsp-file>/WEB-INF/example.jsp</jsp-file> <init-param> <param-name>exampleParameter</param-name> <param-value>parameterValue</param-value> </init-param> </servlet>
к оглавлению
Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?
JSP страницы используются в основном для целей отображения представления (view), а вся бизнес-логика (controller) и модель (model) должны быть реализованы в сервлетах или классах-моделях. Обязанность JSP страницы — создание HTML ответа из переданных через атрибуты параметров. Большая часть JSP содержит HTML код, а для того, чтобы помочь верстальщикам понять JSP код страницы предоставляется функционал элементов action, JSP EL, JSP Standart Tag Library. Именно их и необходимо использовать вместо скриптлетов для создания моста между (JSP)HTML и (JSP)Java частями.
к оглавлению
Можно ли определить класс внутри JSP страницы?
Определить класс внутри JSP страницы можно, но это считается плохой практикой:
<%! private static class ExampleOne { //... } %> <% private class ExampleTwo { //... } %>
к оглавлению
Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?
JSP Expression Language (EL) — скриптовый язык выражений, который позволяет получить доступ к Java компонентам (JavaBeans) из JSP. Начиная с JSP 2.0 используется внутри JSP тегов для отделения Java кода от JSP для обеспечения лёгкого доступа к Java компонентам, уменьшая при этом количество кода Java в JSP-страницах, или даже полностью исключая его.
Развитие EL происходило с целью сделать его более простым для дизайнеров, которые имеют минимальные познания в языке программирования Java. До появления языка выражений, JSP имел несколько специальных тегов таких как скриптлеты (англ.), выражения и т. п. которые позволяли записывать Java код непосредственно на странице. С использованием языка выражений веб-дизайнер должен знать только то, как организовать вызов соответствующих java-методов.
Язык выражений JSP 2.0 включает:
- Создание и изменение переменных.
- Управление потоком выполнения программы: ветвление, выполнение различных типов итераций и т.д.
- Упрощенное обращение к встроенным JSP-объектам.
- Возможность создавать собственные функции.
Язык выражений используется внутри конструкции ${ ... }
. Подобная конструкция может размещаться либо отдельно, либо в правой части выражения установки атрибута тега.
к оглавлению
Какие типы EL операторов вы знаете?
Операторы в EL поддерживают наиболее часто используемые манипуляции данными.
Типы операторов:
- Стандартные операторы отношения:
==
(илиeq
),!=
(илиneq
),<
(илиlt
),>
(илиgt
),<=
(илиle
),>=
(илиge
). - Арифметические операторы:
+
,–
,*
,/
(илиdiv
),%
(илиmod
). - Логические операторы:
&&
(илиand
),||
(илиor
),!
(илиnot
). - Оператор
empty
– используется для проверки переменной наnull
, или «пустое значение», который зависит от типа проверяемого объекта. Например, нулевая длина для строки или нулевой размер для коллекции.
к оглавлению
Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.
Язык выражений JSP предоставляет множество неявных объектов, которые можно использовать для получения атрибутов в различных областях видимости (scopes) и для значений параметров. Важно отметить, что они отличаются от неявных объектов JSP и содержат атрибуты в заданной области видимости. Наиболее часто использующийся implicit object в JSP EL и JSP page — это объект pageContext. Ниже представлена таблица неявных объектов JSP EL.
к оглавлению
Как отключить возможность использования EL в JSP?
Для игнорирования выполнения языка выражений на странице существует два способа:
- использовать директиву
<%@ page isELIgnored = «true» %>
, - настроить
web.xml
(лучше подходит для отключения EL сразу на нескольких страницах):
<jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <el-ignored>true</el-ignored> </jsp-property-group> </jsp-config>
к оглавлению
Как узнать тип HTTP метода используя JSP EL?
${pageContext.request.method}
.
к оглавлению
Что такое JSTL (JSP Standard tag library)?
JavaServer Pages Standard Tag Library, JSTL, Стандартная библиотека тегов JSP — расширение спецификации JSP (конечный результат JSR 52), добавляющее библиотеку JSP тегов для общих нужд, таких как разбор XML данных, условная обработка, создание циклов и поддержка интернационализации.
JSTL является альтернативой такому виду встроенной в JSP логики, как скриплеты (прямые вставки Java кода). Использование стандартизованного множества тегов предпочтительнее, поскольку получаемый код легче поддерживать и проще отделять бизнес-логику от логики отображения.
Для использования JSTL тегов необходимо:
- подключить зависимости, например в
pom.xml
:
<dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency>
- указать пространство имен основных тегов JSTL через указание на JSP странице код:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
к оглавлению
Из каких групп тегов состоит библиотека JSTL?
Группы тегов JSTL согласно их функциональности:
- Core Tags предоставляют возможности итерации, обработки исключений, URL, forward, redirect response и т.д.
- Formatting Tags и Localization Tags предоставляют возможности по форматированию чисел, дат и поддержки i18n локализации и resource bundles.
- SQL Tags – поддержка работы с базами данных.
- XML Tags используются для работы с XML документами: парсинга, преобразования данных, выполнения выражений XPath и т.д..
- JSTL Functions Tags предоставляет набор функций, которые позволяют выполнять различные операции со строками и т.п. Например, по конкатенации или разбиению строк.
к оглавлению
Какая разница между <c:set>
и <jsp:useBean>
?
Оба тега создают и помещают экземпляры в заданную область видимости, но <jsp:useBean>
только создаёт экземпляр конкретного типа, а <c:set>
, создав экземпляр, позволяет дополнительно извлекать значение, например, из параметров запроса, сессии и т. д.
к оглавлению
Чем отличается <c:import>
от <jsp:include>
и директивы <%@include %>
?
По сравнению с action-тегом <jsp:include>
и директивой <%@include %>
тег <c:import>
обеспечивает более совершенное включение динамических ресурсов, т.к. получает доступ к источнику, чтение информации из которого происходит непосредственно без буферизации и контент включается в исходную JSP построчно.
к оглавлению
Как можно расширить функциональность JSP?
Что вы знаете о написании пользовательских JSP тегов?
Приведите пример использования собственных тегов.
JSP можно расширить с помощью создания собственных тегов с необходимой функциональностью, которые можно добавить в библиотеку тегов на страницу JSP указав необходимое пространство имен.
/WEB-INF/exampleTag.tld
<?xml version="1.0" encoding="UTF-8"?> <taglib version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_1.xsd"> <tlib-version>1.0</tlib-version> <short-name>example</short-name> <uri>/WEB-INF/exampleTag</uri> <tag> <name>exampleTag</name> <tag-class>xyz.company.ExampleTag</tag-class> <body-content>empty</body-content> <info>The example tag displays Hello World!</info> </tag> </taglib>
xyz.company.ExampleServlet.java
package xyz.company; import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class ExampleTag extends TagSupport{ private static final long serialVersionUID = 1L; @Override public int doStartTag() throws JspException { try { pageContext.getOut().print("Hello World!"); } catch(IOException ioException) { throw new JspException("Error: " + ioException.getMessage()); } return SKIP_BODY; } }
exampleTag.jsp
<%@ taglib uri="/WEB-INF/exampleTag.tld" prefix="example"%>
<%@ page session="false" pageEncoding="UTF-8"%>
<html>
<head>
<title>Example Tag</title>
</head>
<body>
<h1>Example Tag</h1>
<p><example:exampleTage /><p>
</body>
</html>
Также в пользовательских тегах существует возможность задать входные параметры. Например, существует необходимость отформатировать каким-либо стилем очень длинное число. Для этого можно использовать собственный тег по типу:
<mytags:formatNumber number="123456.789" format="#,## #.00"/>
Используя входные параметры, число должно быть преобразовано на JSP странице в таком виде 123,456.79
согласно шаблону. Т.к. JSTL не предоставляет такой функциональности, необходимо создать пользовательский тег для получения необходимого результата.
к оглавлению
Как сделать перенос строки в HTML средствами JSP?
Для переноса строки можно использовать тег c:out
и атрибут escapeXml
, который отключает обработку HTML элементов. В этом случае браузер получит следующий код в виде строки и обработает элемент <br>
как требуется:
<c:out value="<br> creates a new line in HTML" escapeXml="true"></c:out>
к оглавлению
Почему не нужно конфигурировать стандартные JSP теги в web.xml
?
Стандартные теги JSP не конфигурируются в web.xml
, потому что tld файлы уже находятся внутри каталога /META-INF/
в jar файлах JSTL.
Когда контейнер загружает веб-приложение и находит tld файлы в в jar файле в директории /META-INF/
, то он автоматически настраивает их для непосредственного использования на JSP страницах. Остается только задать пространство имен на JSP странице.
к оглавлению
Как можно обработать ошибки JSP страниц?
Для обработки исключений выброшенных на JSP странице достаточно лишь задать страницу ошибки JSP и при её создании установить значение page directive attribute isErrorPage
в значение true
. Таким образом будет предоставлен доступ к неявным объектам исключений в JSP и появится возможность передавать собственные, более информативные сообщения об ошибках клиенту. При этом настройка дескриптора развертывания выглядит так:
<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error.jsp</location> </error-page>
к оглавлению
Как происходит обработка ошибок с помощью JSTL?
Для перехвата и обработки исключений в служебных методах класса служат JSTL Core Tags c:catch
и c:if
.
Тег
c:catch
перехватывает исключение и обертывает его в переменнуюexception
, доступную для обработки в тегеc:if
:
<c:catch var ="exception">
<% int x = 42/0;%>
</c:catch>
<c:if test = "${exception ne null}">
<p>Exception is : ${exception} <br />
Exception Message: ${exception.message}</p>
</c:if>
Обратите внимание что используется язык выражений JSP EL в теге c:if
.
к оглавлению
Как конфигурируется JSP в дескрипторе развертывания.
Для настройки различных параметров JSP страниц используется элемент jsp-config
, который отвечает за:
- управление элементами скриптлетов на странице;
- управления выполнением в языке выражений;
- определение шаблона URL для encoding;
- определение размера буфера, который используется для объектов на странице;
- обозначение групп ресурсов, соответствующих шаблону URL, которые должны быть обработаны как XML документ.
<jsp-config> <taglib> <taglib-uri>http://company.xyz/jsp/tlds/customtags</taglib-uri> <taglib-location>/WEB-INF/exampleTag.tld</taglib-location> </taglib> </jsp-config>
к оглавлению
Можно ли использовать Javascript на JSP странице?
Да, это возможно. Несмотря на то, что JSP это серверная технология, на выходе она всё равно создает HTML
страницу, на которую можно добавлять Javascript и CSS.
к оглавлению
Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?
Jsp-страница, по умолчанию, всегда создает сессию. Используя директиву page
с атрибутом session
можно изменить это поведение:
<%@ page session ="false" %>
к оглавлению
Какая разница между JSPWriter
и сервлетным PrintWriter
?
PrintWriter
является объектом отвечающим за запись содержания ответа на запрос. JspWriter
использует объект PrintWriter
для буферизации. Когда буфер заполняется или сбрасывается, JspWriter
использует объект PrintWriter
для записи содержания в ответ.
к оглавлению
Опишите общие практические принципы работы с JSP.
Хорошей практикой работы с технологией JSP является соблюдение следующих правил:
- Следует избегать использования элементов скриптлетов на странице. Если элементы action, JSTL, JSP EL не удовлетворяют потребностям, то желательно написать собственный тег.
- Рекомендуется использовать разные виды комментариев: так JSP комментарии необходимы для уровня кода и отладки, т.к. они не будут показаны клиенту.
- Не стоит размещать какой-либо бизнес логики внутри JSP страницы. Страницы должны использоваться только для создания ответов клиенту.
- Для повышения производительности лучше отключать создание сессии на странице, когда это не требуется.
- Директивы
taglib
,page
в начале JSP страницы улучшают читабельность кода. - Следует правильно использовать директиву
include
и элементjsp:include action
. Первая используется для статических ресурсов, а второй для динамических ресурсов времени выполнения. - Обработку исключений нужно производить с помощью страниц ошибок. Это помогает избегать запуска специальных служебных методов и может повысить производительность.
- Использующиеся CSS и JavaScript должны быть разнесены в разные файлы и подключаться в начале страницы.
- В большинстве случаев JSTL должно хватать для всех нужд. Если это не так, то в начале следует проанализировать логику своего приложения, и попробовать перенести выполнения кода в сервлет, а далее с помощью установки атрибутов использовать на JSP странице только результат.
к оглавлению
Источники
- javastudy.ru
- java2ee.ru
- Java-online
- Codenet
- JavaTalks Articles
Вопросы для собеседования
Программирование, JAVA, Параллельное программирование, GitHub, Eclipse
Рекомендация: подборка платных и бесплатных курсов Python — https://katalog-kursov.ru/
Цель публикации показать начинающим Java программистам все этапы создания многопоточного сервера. Для полного понимания данной темы основная информация содержится в комментариях моего кода и в выводимых в консоли сообщениях для лучшего понимания что именно происходит и в какой именно последовательности.
В начале будет рассмотрено создание элементарного клиент-сервера, для усвоения базовых знаний, на основе которых будет строиться многопоточная архитектура.
Понятия.
— Потоки: для того чтобы не перепутать что именно подразумевается под потоком я буду использовать существующий в профессиональной литературе синоним — нить, чтобы не путать Stream и Thread, всё-таки более профессионально выражаться — нить, говоря про Thread.
— Сокеты(Sockets): данное понятие тоже не однозначно, поскольку в какой-то момент сервер выполняет — клиентские действия, а клиент — серверные. Поэтому я разделил понятие серверного сокета — (ServerSocket) и сокета (Socket) через который практически осуществляется общение, его будем называть сокет общения, чтобы было понятно о чём речь.
Кроме того сокетов общения создаётся по одному на каждом из обменивающихся данными приложении, поэтому сокет приложения которое имеет у себя объект - ServerSocket и первоначально открывает порт в ожидании подключения будем называть сокет общения на стороне сервера, а сокет который создаёт подключающееся к порту по известному адресу второе приложение будем называть сокетом общения на стороне клиента.
Спасибо за подсказку про Thread.sleep();!
Конечно в реальном коде Thread.sleep(); устанавливать не нужно — это муветон! В данной публикации я его использую только для того чтобы выполнение программы было нагляднее, что бы успевать разобраться в происходящем.
Так что тестируйте, изучайте и в своём коде никогда не используйте Thread.sleep();!
Оглавление:
1) Однопоточный элементарный сервер.
2) Клиент.
3) Многопоточный сервер – сам по себе этот сервер не участвует в общении напрямую, а лишь является фабрикой однонитевых делегатов(делегированных для ведения диалога с клиентами серверов) для общения с вновь подключившимися клиентами, которые закрываются после окончания общения с клиентом.
4) Имитация множественного обращения клиентов к серверу.
По многочисленным замечаниям выкладываю ссылку на исходники на GitHub:
(https://github.com/merceneryinbox/Clietn-Server_Step-by-step.git)
Итак, начнём с изучения структуры однопоточного сервер, который может принять только одного клиента для диалога. Код приводимый ниже необходимо запускать в своей IDE в этом идея всей статьи. Предлагаю все детали уяснить из подробно задокументированного кода ниже:
- 1) Однопоточный элементарный сервер.
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TestAsServer {
/**
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// стартуем сервер на порту 3345
try (ServerSocket server= new ServerSocket(3345)){
// становимся в ожидание подключения к сокету под именем - "client" на серверной стороне
Socket client = server.accept();
// после хэндшейкинга сервер ассоциирует подключающегося клиента с этим сокетом-соединением
System.out.print("Connection accepted.");
// инициируем каналы для общения в сокете, для сервера
// канал записи в сокет
DataOutputStream out = new DataOutputStream(client.getOutputStream());
System.out.println("DataOutputStream created");
// канал чтения из сокета
DataInputStream in = new DataInputStream(client.getInputStream());
System.out.println("DataInputStream created");
// начинаем диалог с подключенным клиентом в цикле, пока сокет не закрыт
while(!client.isClosed()){
System.out.println("Server reading from channel");
// сервер ждёт в канале чтения (inputstream) получения данных клиента
String entry = in.readUTF();
// после получения данных считывает их
System.out.println("READ from client message - "+entry);
// и выводит в консоль
System.out.println("Server try writing to channel");
// инициализация проверки условия продолжения работы с клиентом по этому сокету по кодовому слову - quit
if(entry.equalsIgnoreCase("quit")){
System.out.println("Client initialize connections suicide ...");
out.writeUTF("Server reply - "+entry + " - OK");
Thread.sleep(3000);
break;
}
// если условие окончания работы не верно - продолжаем работу - отправляем эхо-ответ обратно клиенту
out.writeUTF("Server reply - "+entry + " - OK");
System.out.println("Server Wrote message to client.");
// освобождаем буфер сетевых сообщений (по умолчанию сообщение не сразу отправляется в сеть, а сначала накапливается в специальном буфере сообщений, размер которого определяется конкретными настройками в системе, а метод - flush() отправляет сообщение не дожидаясь наполнения буфера согласно настройкам системы
out.flush();
}
// если условие выхода - верно выключаем соединения
System.out.println("Client disconnected");
System.out.println("Closing connections & channels.");
// закрываем сначала каналы сокета !
in.close();
out.close();
// потом закрываем сам сокет общения на стороне сервера!
client.close();
// потом закрываем сокет сервера который создаёт сокеты общения
// хотя при многопоточном применении его закрывать не нужно
// для возможности поставить этот серверный сокет обратно в ожидание нового подключения
System.out.println("Closing connections & channels - DONE.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 2) Клиент.
Сервер запущен и находится в блокирующем ожидании server.accept(); обращения к нему с запросом на подключение. Теперь можно подключаться клиенту, напишем код клиента и запустим его. Клиент работает когда пользователь вводит что-либо в его консоли (внимание! в данном случае сервер и клиент запускаются на одном компьютере с локальным адресом — localhost, поэтому при вводе строк, которые должен отправлять клиент не забудьте убедиться, что вы переключились в рабочую консоль клиента!).
После ввода строки в консоль клиента и нажатия enter строка проверяется не ввёл ли клиент кодовое слово для окончания общения дальше отправляется серверу, где он читает её и то же проверяет на наличие кодового слова выхода. Оба и клиент и сервер получив кодовое слово закрывают ресурсы после предварительных приготовлений и завершают свою работу.
Посмотрим как это выглядит в коде:
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
public class TestASClient {
/**
*
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// запускаем подключение сокета по известным координатам и нициализируем приём сообщений с консоли клиента
try(Socket socket = new Socket("localhost", 3345);
BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
DataInputStream ois = new DataInputStream(socket.getInputStream()); )
{
System.out.println("Client connected to socket.");
System.out.println();
System.out.println("Client writing channel = oos & reading channel = ois initialized.");
// проверяем живой ли канал и работаем если живой
while(!socket.isOutputShutdown()){
// ждём консоли клиента на предмет появления в ней данных
if(br.ready()){
// данные появились - работаем
System.out.println("Client start writing in channel...");
Thread.sleep(1000);
String clientCommand = br.readLine();
// пишем данные с консоли в канал сокета для сервера
oos.writeUTF(clientCommand);
oos.flush();
System.out.println("Clien sent message " + clientCommand + " to server.");
Thread.sleep(1000);
// ждём чтобы сервер успел прочесть сообщение из сокета и ответить
// проверяем условие выхода из соединения
if(clientCommand.equalsIgnoreCase("quit")){
// если условие выхода достигнуто разъединяемся
System.out.println("Client kill connections");
Thread.sleep(2000);
// смотрим что нам ответил сервер на последок перед закрытием ресурсов
if(ois.available()!=0) {
System.out.println("reading...");
String in = ois.readUTF();
System.out.println(in);
}
// после предварительных приготовлений выходим из цикла записи чтения
break;
}
// если условие разъединения не достигнуто продолжаем работу
System.out.println("Client sent message & start waiting for data from server...");
Thread.sleep(2000);
// проверяем, что нам ответит сервер на сообщение(за предоставленное ему время в паузе он должен был успеть ответить)
if(ois.available()!=0) {
// если успел забираем ответ из канала сервера в сокете и сохраняем её в ois переменную, печатаем на свою клиентскую консоль
System.out.println("reading...");
String in = ois.readUTF();
System.out.println(in);
}
}
}
// на выходе из цикла общения закрываем свои ресурсы
System.out.println("Closing connections & channels on clentSide - DONE.");
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 3) Многопоточный сервер
А что если к серверу хочет подключиться ещё один клиент!? Ведь описанный выше сервер либо находится в ожидании подключения одного клиента, либо общается с ним до завершения соединения, что делать остальным клиентам? Для такого случая нужно создать фабрику которая будет создавать описанных выше серверов при подключении к сокету новых клиентов и не дожидаясь пока делегированный подсервер закончит диалог с клиентом откроет accept() в ожидании следующего клиента. Но чтобы на серверной машине хватило ресурсов для общения со множеством клиентов нужно ограничить количество возможных подключений. Фабрика будет выдавать немного модифицированный вариант предыдущего сервера(модификация будет касаться того что класс сервера для фабрики будет имплементировать интерфейс — Runnable для возможности его использования в пуле нитей — ExecuteServices). Давайте создадим такую серверную фабрику и ознакомимся с подробным описанием её работы в коде:
- Фабрика:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author mercenery
*
*/
public class MultiThreadServer {
static ExecutorService executeIt = Executors.newFixedThreadPool(2);
/**
* @param args
*/
public static void main(String[] args) {
// стартуем сервер на порту 3345 и инициализируем переменную для обработки консольных команд с самого сервера
try (ServerSocket server = new ServerSocket(3345);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
System.out.println("Server socket created, command console reader for listen to server commands");
// стартуем цикл при условии что серверный сокет не закрыт
while (!server.isClosed()) {
// проверяем поступившие комманды из консоли сервера если такие
// были
if (br.ready()) {
System.out.println("Main Server found any messages in channel, let's look at them.");
// если команда - quit то инициализируем закрытие сервера и
// выход из цикла раздачии нитей монопоточных серверов
String serverCommand = br.readLine();
if (serverCommand.equalsIgnoreCase("quit")) {
System.out.println("Main Server initiate exiting...");
server.close();
break;
}
}
// если комманд от сервера нет то становимся в ожидание
// подключения к сокету общения под именем - "clientDialog" на
// серверной стороне
Socket client = server.accept();
// после получения запроса на подключение сервер создаёт сокет
// для общения с клиентом и отправляет его в отдельную нить
// в Runnable(при необходимости можно создать Callable)
// монопоточную нить = сервер - MonoThreadClientHandler и тот
// продолжает общение от лица сервера
executeIt.execute(new MonoThreadClientHandler(client));
System.out.print("Connection accepted.");
}
// закрытие пула нитей после завершения работы всех нитей
executeIt.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Модифицированный Runnable сервер для запуска из предыдущего кода:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class MonoThreadClientHandler implements Runnable {
private static Socket clientDialog;
public MonoThreadClientHandler(Socket client) {
MonoThreadClientHandler.clientDialog = client;
}
@Override
public void run() {
try {
// инициируем каналы общения в сокете, для сервера
// канал записи в сокет следует инициализировать сначала канал чтения для избежания блокировки выполнения программы на ожидании заголовка в сокете
DataOutputStream out = new DataOutputStream(clientDialog.getOutputStream());
// канал чтения из сокета
DataInputStream in = new DataInputStream(clientDialog.getInputStream());
System.out.println("DataInputStream created");
System.out.println("DataOutputStream created");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// основная рабочая часть //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// начинаем диалог с подключенным клиентом в цикле, пока сокет не
// закрыт клиентом
while (!clientDialog.isClosed()) {
System.out.println("Server reading from channel");
// серверная нить ждёт в канале чтения (inputstream) получения
// данных клиента после получения данных считывает их
String entry = in.readUTF();
// и выводит в консоль
System.out.println("READ from clientDialog message - " + entry);
// инициализация проверки условия продолжения работы с клиентом
// по этому сокету по кодовому слову - quit в любом регистре
if (entry.equalsIgnoreCase("quit")) {
// если кодовое слово получено то инициализируется закрытие
// серверной нити
System.out.println("Client initialize connections suicide ...");
out.writeUTF("Server reply - " + entry + " - OK");
Thread.sleep(3000);
break;
}
// если условие окончания работы не верно - продолжаем работу -
// отправляем эхо обратно клиенту
System.out.println("Server try writing to channel");
out.writeUTF("Server reply - " + entry + " - OK");
System.out.println("Server Wrote message to clientDialog.");
// освобождаем буфер сетевых сообщений
out.flush();
// возвращаемся в началло для считывания нового сообщения
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// основная рабочая часть //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// если условие выхода - верно выключаем соединения
System.out.println("Client disconnected");
System.out.println("Closing connections & channels.");
// закрываем сначала каналы сокета !
in.close();
out.close();
// потом закрываем сокет общения с клиентом в нити моносервера
clientDialog.close();
System.out.println("Closing connections & channels - DONE.");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Для имитации множественного обращения клиентов к серверу, создадим и запустим (после запуска серверной части) фабрику Runnable клиентов которые будут подключаться серверу и писать сообщения в цикле:
- 4) Имитация множественного обращения клиентов к серверу.
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
// private static ServerSocket server;
public static void main(String[] args) throws IOException, InterruptedException {
// запустим пул нитей в которых колличество возможных нитей ограничено -
// 10-ю.
ExecutorService exec = Executors.newFixedThreadPool(10);
int j = 0;
// стартуем цикл в котором с паузой в 10 милисекунд стартуем Runnable
// клиентов,
// которые пишут какое-то количество сообщений
while (j < 10) {
j++;
exec.execute(new TestRunnableClientTester());
Thread.sleep(10);
}
// закрываем фабрику
exec.shutdown();
}
}
Как видно из предыдущего кода фабрика запускает — TestRunnableClientTester() клиентов, напишем для них код и после этого запустим саму фабрику, чтобы ей было кого исполнять в своём пуле:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class TestRunnableClientTester implements Runnable {
static Socket socket;
public TestRunnableClientTester() {
try {
// создаём сокет общения на стороне клиента в конструкторе объекта
socket = new Socket("localhost", 3345);
System.out.println("Client connected to socket");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try (
// создаём объект для записи строк в созданный скокет, для
// чтения строк из сокета
// в try-with-resources стиле
DataOutputStream oos = new DataOutputStream(socket.getOutputStream());
DataInputStream ois = new DataInputStream(socket.getInputStream())) {
System.out.println("Client oos & ois initialized");
int i = 0;
// создаём рабочий цикл
while (i < 5) {
// пишем сообщение автогенерируемое циклом клиента в канал
// сокета для сервера
oos.writeUTF("clientCommand " + i);
// проталкиваем сообщение из буфера сетевых сообщений в канал
oos.flush();
// ждём чтобы сервер успел прочесть сообщение из сокета и
// ответить
Thread.sleep(10);
System.out.println("Client wrote & start waiting for data from server...");
// забираем ответ из канала сервера в сокете
// клиента и сохраняем её в ois переменную, печатаем на
// консоль
System.out.println("reading...");
String in = ois.readUTF();
System.out.println(in);
i++;
Thread.sleep(5000);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Запускайте, вносите изменения в код, только так на самом деле можно понять работу этой структуры.
Спасибо за внимание.
22.06.21
Все коды состояния или http-статусы состоят из трех цифр. Они помогают понять, чем в итоге обернулся запрос между пользователем и сервером — успехом или ошибкой. Некоторые из рассматриваемых ниже кодов вам хорошо знакомы, но есть и совсем редкие.
Всего существует пять групп серверных ответов, которые определяют класс состояния по первой цифре. Так можно сразу определить формат ответа от информационного до сообщения об ошибке.
1xx – Информационные
2xx – Успешные коды ответа
3xx – Коды редиректов
4xx – Ошибки со стороны клиента
5xx – Ошибки со стороны сервера
Информационные коды
Данная группа отвечает за передачу данных. Коды этого типа свидетельствуют о том, что запрос принят сервером и обрабатывается.
100 — Continue — Временный код ответа, означающий начало принятия запроса к его последующей обработке.
101 — Switching Protocols — Сообщает о переключении сервера на протокол, которые был указан в заголовке Upgrade запроса клиента.
102 — Processing — Информация о том, что запрос принят сервером и находится в обработке, но этот процесс еще не завершен.
103 — Early Hints — Используется для предварительной загрузки данных пока сервер формирует полный ответ.
Успешная обработка запроса
Коды группы сообщают, что запрос не только принят сервером, но и успешно обработан.
200 — OK — Один из самых популярных ответов. Он свидетельствует о том, что обмен данными между клиентом и сервером прошли успешно. Именно код ответа 200 ждут от ресурса, чтобы проверить, что все работает как надо: сайт загружен, файл открывается и т.д.
201 — Created — Информирует об успешном создании нового ресурса в результате выполнения запроса. Например, была создана новая страница. Если сервер по каким-то причинам не смог обработать запрос и ресурс не был создан, то код ответа будет 202.
202 — Accepted — Сообщает, что сервер принял запрос, но не завершил его обработку.
203 — Non-Authoritative Information — Отвечает об успешной обработке запроса с оговоркой на то, что передаваемая информация была предоставлена не из исходного сервера, а другого источника (например, резервной копии) и может быть неактуальной.
204 — No Content — Сообщает об успешном принятии и обработке запроса, а также о том, что у сервера нет содержимого для отправки пользователю.
205 — Reset Content — Сервер передает пользователю ответ в виде требований к сбросу введенных данных. Например, о необходимости очистить форму с заполненными до этого данными.
206 — Partial Content — Свидетельствует о частичном выполнении GET-запроса сервером, возвращая только запрошенную пользователем часть контента. Этот код встречается при использовании кэширования.
Коды редиректов
Данная группа кодов состояния сообщает о перенаправлении пользователя с его согласием или без него.
300 — Multiple Choices — Ответ срабатывает при условии, что по указанному запросу существует несколько вариантов URL. При таком варианте пользователь или User-agent должен выбрать альтернативный адрес.
301 — Moved Permanently — Свидетельствует о перемещении ранее проиндексированного URL на новый адрес. Это команда роботу индексировать новую страницу вместо старой.
301 редирект также используемся вебмастерами для перенаправления сайта с http://www.mixtelecom.ru на http://mixtelecom.ru. Применяется при переходе на защищенный протокол с HHTP на HTTPS. Используется для защиты доступа к внутренним файлам, например, /index.php переправляется на главный адрес сайта. Некоторые хостинги и CMS автоматически включают данные редиректы при выборе соответствующих опций при настройке сайта.
302 — Found, 302 Moved Temporarily — Сообщает, что ранее проиндексированный URL был временно перемещен по другому адресу. При этом страница остается в индексе, а в ответе указывается новый адрес запрашиваемого URL.
303 — See Other — Указывает пользователю, что запрошенная страница находится по другому адресу с запросом GET.
304 — Not Modified — Показывает, что запрашиваемая страница или объект не были изменены с момента последнего обновления кэша данного документа.
305 — Use Proxy — Сообщает пользователю, что запрашиваемый ресурс доступен только через прокси. Данные по прокси указаны в ответе сервера.
307 — Temporary Redirect — Код схож с 302, сообщая о временном перемещении ресурса на другой адрес. Разница заключается в способе обращения к ресурсу, который должен быть получен тем же методом, что и предыдущий запрос.
Ошибки клиента
Коды состояний данной группы сообщают об ошибках клиента, при которых сервер не может вызвать запрашиваемый результат.
400 — Bad Request — Ошибка свидетельствует от том, что сервер не понял запрос пользователя из-за синтаксической ошибки.
401 — Unauthorized — Сообщает о необходимости быть авторизованным для получения запрашиваемого доступа. Возникает при неправильном вводе данных пользователем при авторизации.
403 — Forbidden — Запрет доступа к запрашиваемой странице. Доступ может быть ограничен настройками индексации или запрещен для определенных IP.
404 — Пожалуй, самая распространенная ошибка. Сообщает о том, что запрашиваемая страница не найдена. Самая частая причина — ошибка в написании адреса.
Когда что-то пошло не так и пользователь не попал по запрашиваемому адресу куда хотел, позаботьтесь, чтобы на сайте была соответствующая страница для ошибки 404. На ней должна быть ссылка на главную страницу или меню ресурса, находиться поиск по сайту или контакты для связи. Если запрашиваемый URL был изменен, то настройте 301-редирект со старого адрес страницы на обновленный.
405 — Method Not Allowed — Сообщает, что в запросе используется метод, который не поддерживается сервером.
406 — Not Acceptable — Указывает, что запрашиваемый пользователем контент не может быть распознан. Причины могут быть в кодировке, методе сжатия или формате объекта.
407 — Proxy Authentication Required — Сообщает, что доступ может быть предоставлен только при авторизации через прокси-сервер.
408 — Request Timeout — Сервер прервал соединение с пользователем из-за слишком долгого ожидания. Данный ответ не возвращается если пользователь принудительно отменил запрос или соединение прервалось по иным причинам.
409 — Conflict — Посылаемый пользователем запрос вызывает конфликт с сервером или другим обращением.
410 — Gone — Ответ сервера при запросе к странице или объекту, который был удален и более недоступен.
411 — Length Required — Отказ сервера на обработку запроса если в нем не указан Content-Length заголовка.
413 — Request Entity Too Large — Сервер не может обработать обращение из-за слишком большого размера запроса.
414 — Request-URL Too Long — Сервер не может обработать обращение если в запросе указан слишком длинный URL.
415 — Unsupported Media Type — Формат запроса пользователя не может быть обработан. Такое встречается при загрузке данных неподходящего формата.
416 — Requested Range Not Satisfiable — Отказ сервера на выполнение запроса из-за некорректного значения поля Range.
417 — Expectation Failed — Отказ сервера на выполнение запроса из-за некорректного значения поля Expect.
422— Unprocessable Entity — Сервер принял и распознал запрос, но не может его выполнить из-за наличия логической ошибки.
423 — Locked — Запрашиваемая пользователем страница заблокирована. Как правило, это делается для защиты содержимого данной страницы или объекта.
424 — Failed Dependency — Выполнение текущего запроса зависит от исхода других связанных с ней операций. Если условия не будут соблюдены, то соединение будет разорвано.
426 — Upgrade Required — Ошибка указывает на необходимость обновить протокол. Встречается, когда сервер запрашивает https-соединение, которое не может быть предоставлено клиентом.
429 — Too Many Requests — Возникает при превышении лимита отправляемых пользователем запросов за короткий промежуток времени. Часто используется настройками безопасности.
Ошибки сервера
В эту группу входят коды ошибок со стороны сервера, когда по тем или иным причинам он не способен обработать запрос или выполнить требуемую операцию.
500 — Internal Server Error — Код оповещает о возникшей внутренней ошибке сервера или его аварийном отказе.
501 — Not Implemented — Сервер столкнулся с запросом, который не смог распознать. Либо запрос не поддерживается и не может быть обработан.
502 — Bad Gateway — Сообщает о неправильном получении ответа вышестоящего сервера. Частая причина — несогласованные протоколов между шлюзом и сервером (ошибки DNS, прокси, хостинга).
Если ваш сайт выдает ошибку 502, то вероятнее всего вносились правки или совершались обновления системы. Просмотрите логи для выявления причины. Если на сайте не было никаких работ и апдейтов, то проверьте ресурсы памяти, процессора, php-лимиты сервера. Возможно их недостаточно для корректной обработки запросов. Причинами также могут быть подключенные CDN или anti-DDoS сервисы. Обратитесь в техподдержку.
503 — Service Unavailable — Указывает на временную недоступность сервера. Причиной может быть его перезагрузка, техническое обслуживание, обращение слишком большого количества пользователей при наличии подобных ограничений. Как правило, сообщение об ошибке содержит параметр Retry-After, информирующий о времени восстановления штатной работы ресурса.
504 — Gateway Time-out — Истек срок ожидания ответа от вышестоящего сервера. Возможные причины: недостаток ресурсов, неполадки с сетевым соединением, ошибки HTTP протокола, настроен слишком короткий срок ожидания.
505 — HTTP Version Not Supported — Используемая в запросе версия протокола HTTP не поддерживается сервером. Встречается при использовании устаревшего формата HTTP-протокола.
506 — Variant Also Negotiates — Сервер не может обработать запрос из-за его неправильной настройки. Сервер зацикливает ответ на себя, выдавая ошибку.
507 — Insufficient Storage — Означает нехватку места на сервере для обработки запросов пользователя. Нужно освободить или увеличить память, либо обратиться в техническую поддержку.
508 — Loop Detected — Ошибка возникает в связи с бесконечным перенаправлением. При обработке запроса возникает петля, что приводит к завершению операции.
509 — Bandwidth Limit Exceeded — Превышен установленный лимит потребления трафика. Ошибка актуальная для интернет-каналов с ограничением по трафику.
510 — Not Extended — Сервер не поддерживает и не может отработать запрашиваемое пользователем расширение. В теле ошибки может быть приведен список доступных расширений.
511 — Network Authentication Required — Сообщает о необходимости авторизации для доступа к сети. Например, если пользователь не авторизовался при подключении к Wi-Fi.
Благодаря приведенным выше кодам ответов намного легче понимать статус сервера при взаимодействии с ним. На практике большинство из них используются в виде указаний роботам по работе с сайтом и порядку его индексации. Сервисы мониторинга и технического анализа сайтов также ориентируются на коды ответов сервера для определения корректной работы ресурса. Вебмастеры могут опираться на статусы состояния в рамках настройки и поддержания работы сайтов. Системные администраторы могут понимать в чем причина неполадок и быстрее устранять возникшие неполадки. Если у вас возникли сложности, которые не получается решить самостоятельно, — воспользуйтесь услугой системного администрирования.
Уровень сложности
Простой
Время на прочтение
9 мин
Количество просмотров 6.6K
Привет! Меня зовут Ивасюта Алексей, я техлид команды Bricks в Авито в кластере Architecture. Я решил написать цикл статей об истории и развитии HTTP, рассмотреть каждую из его версий и проблемы, которые они решали и решают сейчас.
Весь современный веб построен на протоколе HTTP. Каждый сайт использует его для общения клиента с сервером. Между собой сервера тоже часто общаются по этому протоколу. На данный момент существует четыре его версии и все они до сих пор используются. Поэтому статьи будут полезны инженерам любых уровней и специализаций, и помогут систематизировать знания об этой важной технологии.
Что такое HTTP
HTTP — это гипертекстовый протокол передачи данных прикладного уровня в сетевой модели OSI. Его представил миру Тим Бернерс-Ли в марте 1991 года. Главная особенность HTTP — представление всех данных в нём в виде простого текста. Через HTTP разные узлы в сети общаются между собой. Модель клиент-серверного взаимодействия классическая: клиент посылает запрос серверу, сервер обрабатывает запрос и возвращает ответ клиенту. Полученный ответ клиент обрабатывает и решает: прекратить взаимодействие или продолжить отправлять запросы.
Ещё одна особенность: протокол не сохраняет состояние между запросами. Каждый запрос от клиента для сервера — отдельная транзакция. Когда поступают два соседних запроса, сервер не понимает, от одного и того же клиента они поступили, или от разных. Такой подход значительно упрощает построение архитектуры веб-серверов.
Как правило, передача данных по HTTP осуществляется через открытое TCP/IP-соединение1. Серверное программное обеспечение по умолчанию обычно использует TCP-порт 80 для работы веб-сервера, а порт 443 — для HTTPS-соединений.
HTTPS (HTTP Secure) — это надстройка над протоколом HTTP, которая поддерживает шифрование посредством криптографических протоколов SSL и TLS. Они шифруют отправляемые данные на клиенте и дешифруют их на сервере. Это защищает данные от чтения злоумышленниками, даже если им удастся их перехватить.
HTTP/0.9
В 1991 году была опубликована первая версия протокола с названием HTTP/0.9. Эта реализация была проста, как топор. От интернет-ресурса того времени требовалось только загружать запрашиваемую HTML-страницу и HTTP/0.9 справлялся с этой задачей. Обычный запрос к серверу выглядел так:
GET /http-spec.html
В протоколе был определен единственный метод GET и и указывался путь к ресурсу. Так пользователи получали страничку. После этого открытое соединение сразу закрывалось.
HTTP/1.0
Годы шли и интернет менялся. Стало понятно, что нужно не только получать странички от сервера, но и отправлять ему данные. В 1996 году вышла версия протокола 1.0.
Что изменилось:
-
В запросе теперь надо было указывать версию протокола. Так сервер мог понимать, как обрабатывать полученные данные.
-
В ответе от сервера появился статус завершения обработки запроса.
-
К запросу и ответу добавился новый блок с заголовками.
-
Добавили поддержку новых методов:
-
HEAD запрашивает ресурс так же, как и метод GET, но без тела ответа. Так можно получить только заголовки, без самого ресурса.
-
POST добавляет сущность к определённому ресурсу. Часто вызывает изменение состояния или побочные эффекты на сервере. Например, так можно отправить запрос на добавление нового поста в блог.
Структура запроса
Простой пример запроса:
GET /path HTTP/1.0
Content-Type: text/html; charset=utf-8
Content-Length: 4
X-Custom-Header: value
test
В первой строчке указаны метод запроса — GET
, путь к ресурсу — /path
и версия протокола — HTTP/1.0
.
Далее идёт блок заголовков. Заголовки — это пары ключ: значение
, каждая из которых записывается с новой строки и разделяется двоеточием. Они передают дополнительные данные и настройки от клиента к серверу и обратно.
HTTP — это текстовый протокол, поэтому и все данные передаются в виде текста. Заголовки можно отделить друг от друга только переносом строки. Нельзя использовать запятые, точку с запятой, или другие разделители. Всё, что идет после имени заголовка с двоеточием и до переноса строки, будет считаться значением заголовка2.
В примере серверу передали три заголовка:
-
Content-Type
— стандартный заголовок. Показывает, в каком формате будут передаваться данные в теле запроса или ответа. -
Content-Length
— сообщает длину контента в теле запроса в байтах. -
X-Custom-Header
— пользовательские заголовки, начинающиеся сX-
с произвольными именем. Через них реализуется специфическая логика обработки для конкретного сервера. Если веб-сервер не поддерживает такие заголовки, то он проигнорирует их.
После блока заголовков идёт тело запроса, в котором передается текст test
.
А так может выглядеть ответ от сервера:
HTTP/1.1 200 OK
Date: Thu, 29 Jul 2021 19:20:01 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 2
Connection: close
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
OK
В первой строке — версия протокола и статус ответа, например, 200 ОК
. Далее идут заголовки ответа. После блока заголовков — тело ответа, в котором записан текст OK
.
Статусы ответов
Клиенту зачастую недостаточно просто отправить запрос на сервер. Во многих случаях надо дождаться ответа и понять, как сервер обработал запрос. Для этого были придуманы статусы ответов. Это трёхзначные числовые коды с небольшими текстовыми обозначениями. Их можно увидеть в терминале или браузере. Сами коды делятся на 5 классов:
-
Информационные ответы: коды 100–199
-
Успешные ответы: коды 200–299
-
Редиректы: коды 300–399
-
Клиентские ошибки: коды 400–499
-
Серверные ошибки: коды 500–599
Мы рассмотрим основные коды, которые чаще всего встречаются в реальных задачах. С остальными более подробно можно ознакомиться в реестре кодов состояния HTTP.
Информационные ответы
100 Continue — промежуточный ответ. Он указывает, что запрос успешно принят. Клиент может продолжать присылать запросы или проигнорировать этот ответ, если запрос был завершён.
Примечание
Этот код ответа доступен с версии HTTP/1.1.
101 Switching Protocol присылается в ответ на запрос, в котором есть заголовок Upgrade. Это означает, что сервер переключился на протокол, который был указан в заголовке. Такая методика используется, например, для переключения на протокол Websocket.
102 Processing — запрос получен сервером, но его обработка ещё не завершена.
Успешные ответы
200 OK — запрос принят и корректно обработан веб-сервером.
201 Created — запрос корректно обработан и в результате был создан новый ресурс. Обычно он присылается в ответ на POST запрос.
Редиректы
301 Moved Permanently — запрашиваемый ресурс на постоянной основе переехал на новый адрес. Тогда новый путь к ресурсу указывается сервером в заголовке Location
ответа.
Примечание
Клиент может изменить метод последующего запроса с POST на GET.
302 Found — указывает, что целевой ресурс временно доступен по другому URl. Адрес перенаправления может быть изменен в любое время, а клиент должен продолжать использовать действующий URI для будущих запросов. Тогда временный путь к ресурсу указывается сервером в заголовке Location
ответа.
Примечание
Клиент может изменить метод последующего запроса с POST на GET.
307 Temporary Redirect — имеет то же значение, что и код 302, за исключением того, что клиент не может менять метод последующего запроса.
308 Permanent Redirect — имеет то же значение, что и код 301, за исключением того, что клиент не может менять метод последующего запроса.
Клиентские ошибки
400 Bad Request — запрос от клиента к веб-серверу составлен некорректно. Обычно это происходит, если клиент не передаёт необходимые заголовки или параметры.
401 Unauthorized — получение запрашиваемого ресурса доступно только аутентифицированным пользователям.
403 Forbidden — у клиента не хватает прав для получения запрашиваемого ресурса. Например, когда обычный пользователь сайта пытается получить доступ к панели администратора.
404 Not Found — сервер не смог найти запрашиваемый ресурс.
405 Method Not Allowed — сервер знает о существовании HTTP-метода, который был указан в запросе, но не поддерживает его. В таком случае сервер должен вернуть список поддерживаемых методов в заголовке Allow
ответа.
Серверные ошибки
500 Internal Server Error — на сервере произошла непредвиденная ошибка.
501 Not Implemented — метод запроса не поддерживается сервером и не может быть обработан.
502 Bad Gateway — сервер, действуя как шлюз или прокси, получил недопустимый ответ от входящего сервера, к которому он обращался при попытке выполнить запрос.
503 Service Unavailable — сервер не готов обработать запрос (например, из-за технического обслуживания или перегрузки). Обратите внимание, что вместе с этим ответом должна быть отправлена удобная страница с объяснением проблемы. Этот ответ следует использовать для временных условий, а HTTP-заголовок Retry-After
по возможности должен содержать расчётное время до восстановления службы.
504 Gateway Timeout — этот ответ об ошибке выдается, когда сервер действует как шлюз и не может получить ответ за отведенное время.
505 HTTP Version Not Supported — версия HTTP, используемая в запросе, не поддерживается сервером.
В HTTP из всего диапазона кодов используется совсем немного. Те коды, которые не используются для задания определенной логики в спецификации, являются неназначенными и могут использоваться веб-серверами для определения своей специфической логики. Это значит, что вы можете, например, придать коду 513 значение «Произошла ошибка обработки видео», или любое другое. Неназначенные коды вы можете посмотреть в реестре кодов состояния HTTP.3
Тело запроса и ответа
Тело запроса опционально и всегда отделяется от заголовков пустой строкой. А как понять, где оно заканчивается? Всё кажется очевидным: где кончается строка, там и заканчивается тело. Однако, два символа переноса строки в HTTP означают конец запроса и отправляют его на сервер. Как быть, если мы хотим передать в теле текст, в котором есть несколько абзацев с разрывами в две строки?
POST /path HTTP/1.1
Host: localhost
Первая строка
Вторая строка после разрыва
По логике работы HTTP соединение отправится сразу после второй пустой строки и сервер получит в качестве данных только строку Первая строка
. Описанную проблему решает специальный заголовок Content-Length
. Он указывает на длину контента в байтах. Обычно клиенты (например, браузеры) автоматически считают длину передаваемых данных и добавляют к запросу заголовок с этим значением. Когда сервер получит запрос, он будет ожидать в качестве контента ровно столько байт, сколько указано в заголовке.
Однако, этого недостаточно для того, чтобы передать данные на сервер. Поведение зависит от реализации сервера, но для большинства из них необходимо также передать заголовок Content-Type. Он указывает на тип передаваемых данных. В качестве значения для этого заголовка используют MIME-типы.4
MIME (Multipurpose Internet Mail Extensions, многоцелевые расширения интернет-почты) — стандарт, который является частью протокола HTTP. Задача MIME — идентифицировать тип содержимого документа по его заголовку. К примеру, текстовый файл имеет тип text/plain
, а HTML-файл — text/html
.
Для передачи данных в формате обычного текста надо указать заголовок Content-Type: text/plain
, а для JSON — Content-Type: application/json
.
Можно ли передать тело с GET-запросом?
Популярный вопрос на некоторых собеседованиях: «Можно ли передать тело с GET-запросом?». Правильный ответ — да. Тело запроса не зависит от метода. В стандарте не описана возможность принимать тело запроса у GET-метода, но это и не запрещено. Технически вы можете отправить данные в теле, но скорее всего веб-сервер будет их игнорировать.
Представим, что на абстрактном сайте есть форма аутентификации пользователя, в которой есть всего два поля: email и пароль.
Если пользователь ввёл данные и нажал на кнопку «Войти», то данные из полей формы должны попасть на сервер. Самым простым и распространенным форматом передачи таких данных будет MIME application/x-www-form-urlencoded
. В нем все поля передаются в одной строке в формате ключ=значение
и разделяются знаком &
.
Запрос на отправку данных будет выглядеть так:
POST /login HTTP/1.0
Host: example.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 26
login=user&password=qwerty
Тут есть небольшая особенность. Как понять, где заканчивается ключ и начинается его значение, если в пароле будет присутствовать знак «=» ?
POST /login HTTP/1.0
Host: example.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 26
login=user&password=123=45
В этом случае сервер не сможет понять, как разбить строку на параметры и их значения. На самом деле значения кодируются при помощи механизма url encoding.5 При использовании этого механизма знак «=» будет преобразован в код %3D
.
Тогда наш запрос приобретёт такой вид:
POST /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 28
login=user&password=123%3D45
Query string
Данные на сервер можно передавать через тело запроса и через так называемую строку запроса Query String. Это параметры формата ключ=значение, которые располагаются в пути к ресурсу:
GET /files?key=value&key2=value2 HTTP/1.0
При этом параметры можно передавать прямо от корня домена:
GET /?key=value&key2=value2 HTTP/1.0
Query String имеет такой же формат, как и тело запроса с MIME application/x-www-form-urlencoded
, только первая пара значений отделяется от адреса вопросительным знаком.
Некоторые инженеры ошибочно полагают, что Query String являются параметрами GET-запроса и даже называют их GET-параметрами, но на самом деле это не так. Как и тело запроса, Query String не имеет привязки к HTTP-методам и может передаваться с любым типом запросов.
Обычно параметры Query String используются в GET-запросах, чтобы конкретизировать получаемый ресурс. Например, можно получить на сервере список файлов, имена которых будут начинаться с переданного значения.
GET-запросы по своей идеологии должны быть идемпотентными. Это значит, что множественный вызов метода с одними и теми же параметрами должен приводить к одному и тому же результату. Например, поисковые боты перемещаются по сайту только по ссылкам и делают только GET-запросы, потому что исходя из семантики они не смогут таким образом изменить данные на сайте и повлиять на его работу.
На этом я закончу говорить про версию протокола 1.0, структуру ответов и запросов. В следующей статье я расскажу, что такое Cookies, для чего нужен CORS и как это всё работает. А пока напоследок оставлю полезные ссылки, которые упомянул в тексте:
-
Основы TCP/IP
-
Заголовки HTTP
-
Реестр кодов состояния HTTP
-
MIME типы
-
Алгоритм кодирования URL encoding
Предыдущая статья: Шесть проблем UX-редакции, которые поможет решить планирование
Это список кодов состояния ответа протокола передачи гипертекста (HTTP). Коды состояния выдаются сервером в ответ на запрос клиента к серверу. Он включает коды из запроса комментариев IETF (RFC), другие спецификации и некоторые дополнительные коды, используемые в некоторых распространенных приложениях HTTP. Первая цифра кода состояния указывает один из пяти стандартных классов ответов. Показанные фразы сообщений являются типичными, но может быть предоставлена любая удобочитаемая альтернатива. Если не указано иное, код состояния является частью стандарта HTTP / 1.1 (RFC 7231).
Управление по присвоению номеров в Интернете (IANA) ведет официальный реестр кодов состояния HTTP.
Все коды состояния ответа HTTP разделены на пять классов или категорий. Первая цифра кода состояния определяет класс ответа, в то время как последние две цифры не имеют никакой роли классификации или категоризации. Стандарт определяет пять классов:
- Информационный ответ 1xx — запрос получен, процесс продолжается
- 2xx успешно — запрос был успешно получен, понят и принят
- Перенаправление 3xx — для выполнения запроса необходимо предпринять дальнейшие действия
- Ошибка клиента 4xx — запрос содержит неверный синтаксис или не может быть выполнен
- Ошибка сервера 5xx — серверу не удалось выполнить явно действительный запрос
Информационный ответ 1xx
Информационный ответ означает, что запрос был получен и понят. Он выдается на временной основе, пока продолжается обработка запроса. Он предупреждает клиента, чтобы он дождался окончательного ответа. Сообщение состоит только из строки состояния и необязательных полей заголовка и заканчивается пустой строкой. Поскольку в стандарте HTTP / 1.0 не определены коды состояния 1xx, серверы не должны отправлять ответ 1xx клиенту, совместимому с HTTP / 1.0, за исключением экспериментальных условий.
- 100 Продолжить
- Сервер получил заголовки запроса, и клиент должен перейти к отправке тела запроса (в случае запроса, для которого необходимо отправить тело; например, запрос POST ). Отправка большого тела запроса на сервер после того, как запрос был отклонен из-за несоответствующих заголовков, будет неэффективен. Чтобы сервер проверил заголовки запроса, клиент должен отправить
Expect: 100-continue
в качестве заголовка в своем первоначальном запросе и получить100 Continue
код состояния в ответ перед отправкой тела. Если клиент получает код ошибки, такой как 403 (Запрещено) или 405 (Метод запрещен), он не должен отправлять тело запроса. В ответе417 Expectation Failed
указано, что запрос следует повторить безExpect
заголовка, поскольку он указывает на то, что сервер не поддерживает ожидания (например, в случае серверов HTTP / 1.0). - 101 протокол переключения
- Запрашивающая сторона попросила сервер переключить протоколы, и сервер дал согласие на это.
- 102 Обработка ( WebDAV ; RFC 2518)
- Запрос WebDAV может содержать множество подзапросов, связанных с файловыми операциями, для выполнения которых требуется много времени. Этот код указывает, что сервер получил и обрабатывает запрос, но ответа пока нет. Это предотвращает тайм-аут клиента и предположение, что запрос был потерян.
- 103 Ранние подсказки (RFC 8297)
- Используется для возврата некоторых заголовков ответа перед окончательным HTTP-сообщением.
2хх успех
Этот класс кодов состояния указывает, что действие, запрошенное клиентом, было получено, понято и принято.
- 200 ОК
- Стандартный ответ на успешные HTTP-запросы. Фактический ответ будет зависеть от используемого метода запроса. В запросе GET ответ будет содержать объект, соответствующий запрошенному ресурсу. В запросе POST ответ будет содержать объект, описывающий или содержащий результат действия.
- 201 Создано
- Запрос выполнен, в результате чего создан новый ресурс.
- 202 Принято
- Запрос принят в обработку, но обработка не завершена. Запрос может или не может быть в конечном итоге обработан, и может быть отклонен, когда происходит обработка.
- 203 Неавторизованная информация (начиная с HTTP / 1.1)
- Сервер представляет собой преобразующий прокси (например, веб-ускоритель ), который получил 200 OK от своего источника, но возвращает измененную версию ответа источника.
- 204 Нет содержимого
- Сервер успешно обработал запрос и не возвращает никакого содержимого.
- 205 Сбросить содержимое
- Сервер успешно обработал запрос, просит, чтобы инициатор запроса сбросил представление документа, и не возвращает никакого содержимого.
- 206 Частичное содержимое (RFC 7233)
- Сервер доставляет только часть ресурса ( обслуживание байтов ) из-за заголовка диапазона, отправленного клиентом. Заголовок диапазона используется HTTP-клиентами для возобновления прерванных загрузок или разделения загрузки на несколько одновременных потоков.
- 207 Мульти-статус (WebDAV; RFC 4918)
- Нижеследующее тело сообщения по умолчанию является XML- сообщением и может содержать несколько отдельных кодов ответа, в зависимости от того, сколько подзапросов было сделано.
- 208 уже сообщено (WebDAV; RFC 5842)
- Члены привязки DAV уже были перечислены в предыдущей части (мультистатусного) ответа и не включаются снова.
- 226 IM используется (RFC 3229)
- Сервер выполнил запрос ресурса, и ответ является представлением результата одной или нескольких манипуляций с экземпляром, примененных к текущему экземпляру.
Перенаправление 3xx
Этот класс кода состояния указывает, что клиент должен предпринять дополнительные действия для выполнения запроса. Многие из этих кодов состояния используются при перенаправлении URL-адресов .
Пользовательский агент может выполнить дополнительное действие без взаимодействия с пользователем, только если во втором запросе используется метод GET или HEAD. Пользовательский агент может автоматически перенаправить запрос. Пользовательский агент должен обнаруживать и вмешиваться, чтобы предотвратить циклические перенаправления.
- 300 вариантов выбора
- Указывает несколько вариантов ресурса, из которых клиент может выбрать (через согласование содержимого, управляемое агентом ). Например, этот код можно использовать для представления нескольких параметров формата видео, для перечисления файлов с разными расширениями файлов или для устранения неоднозначности в словах .
- 301 перемещен навсегда
- Этот и все будущие запросы должны быть направлены на данный URI .
- 302 найдено (ранее «перемещено временно»)
- Сообщает клиенту, что нужно посмотреть (перейти) на другой URL-адрес. Спецификация HTTP / 1.0 (RFC 1945) требовала, чтобы клиент выполнял временное перенаправление с помощью того же метода (исходная описывающая фраза была «Перемещено временно»), но популярные браузеры реализовали 302 перенаправления, изменив метод на GET. Поэтому в HTTP / 1.1 добавлены коды состояния 303 и 307, чтобы различать два поведения.
- 303 См. Другое (начиная с HTTP / 1.1)
- Ответ на запрос можно найти под другим URI с помощью метода GET. При получении в ответ на POST (или PUT / DELETE) клиент должен предполагать, что сервер получил данные, и должен отправить новый запрос GET для данного URI.
- 304 Не изменено (RFC 7232)
- Указывает, что ресурс не был изменен с версии, указанной в заголовках запроса If-Modified-Since или If-None-Match. В таком случае нет необходимости повторно передавать ресурс, поскольку у клиента все еще есть ранее загруженная копия.
- 305 Использовать прокси (начиная с HTTP / 1.1)
- Запрошенный ресурс доступен только через прокси, адрес которого указан в ответе. По соображениям безопасности многие HTTP-клиенты (например, Mozilla Firefox и Internet Explorer ) не подчиняются этому коду состояния.
- 306 Переключить прокси
- Больше не используется. Изначально означало «Последующие запросы должны использовать указанный прокси».
- 307 Временное перенаправление (начиная с HTTP / 1.1)
- В этом случае запрос следует повторить с другим URI; однако в будущих запросах должен по-прежнему использоваться исходный URI. В отличие от того, как 302 был исторически реализован, метод запроса не может быть изменен при повторной выдаче исходного запроса. Например, запрос POST следует повторить, используя другой запрос POST.
- 308 постоянное перенаправление (RFC 7538)
- Этот и все будущие запросы должны быть направлены на данный URI . 308 аналогичен поведению 301, но не позволяет изменять метод HTTP . Так, например, отправка формы на постоянно перенаправляемый ресурс может продолжаться гладко.
4хх клиентских ошибок
Этот класс кода состояния предназначен для ситуаций, в которых кажется, что ошибка была вызвана клиентом. За исключением ответа на запрос HEAD, сервер должен включать объект, содержащий объяснение ситуации с ошибкой и того, является ли это временным или постоянным состоянием. Эти коды состояния применимы к любому методу запроса. Пользовательские агенты должны отображать пользователю любую включенную сущность.
- ошибка 400, неверный запрос
- Сервер не может или не будет обрабатывать запрос из-за очевидной ошибки клиента (например, неверный синтаксис запроса, слишком большой размер, недопустимое формирование сообщения запроса или обманчивая маршрутизация запроса).
- 401 неавторизованный (RFC 7235)
- Подобно 403 Forbidden , но специально для использования, когда аутентификация требуется, но она не удалась или еще не была предоставлена. Ответ должен включать поле заголовка WWW-Authenticate, содержащее запрос, применимый к запрошенному ресурсу. См. Разделы Аутентификация базового доступа и Дайджест-аутентификация доступа . 401 семантически означает «неавторизованный», у пользователя нет действительных учетных данных для аутентификации для целевого ресурса.
- Примечание. Некоторые сайты неправильно выдают HTTP 401, когда IP-адрес запрещен для доступа к веб-сайту (обычно это домен веб-сайта) и этому конкретному адресу отказано в разрешении на доступ к веб-сайту.
- 402 Требуется оплата
- Зарезервировано для использования в будущем. Первоначальное намерение состояло в том, чтобы этот код можно было использовать как часть какой-либо формы цифровых денег или схемы микроплатежей , как было предложено, например, GNU Taler , но этого еще не произошло, и этот код широко не используется. Google Developers API использует этот статус, если конкретный разработчик превысил дневной лимит запросов. Sipgate использует этот код, если на счете недостаточно средств для начала звонка. Shopify использует этот код, когда магазин не выплатил комиссию и временно отключен. Stripe использует этот код для неудачных платежей с правильными параметрами, например, заблокированных мошеннических платежей.
- 403 Запрещено
- Запрос содержал действительные данные и был понят сервером, но сервер отклоняет действие. Это может быть связано с тем, что пользователь не имеет необходимых разрешений для ресурса или ему нужна учетная запись какого-либо типа, или он пытается выполнить запрещенное действие (например, создает дублирующую запись, где разрешен только один). Этот код также обычно используется, если запрос предоставил аутентификацию путем ответа на запрос поля заголовка WWW-Authenticate, но сервер не принял эту аутентификацию. Запрос не должен повторяться.
- 404 Не Найдено
- Запрошенный ресурс не может быть найден, но может быть доступен в будущем. Последующие запросы клиента допустимы.
- 405 Метод запрещен
- Метод запроса не поддерживается для запрошенного ресурса; например, запрос GET в форме, которая требует, чтобы данные были представлены через POST , или запрос PUT для ресурса, доступного только для чтения.
- 406 неприемлемо
- Запрошенный ресурс может генерировать только контент, неприемлемый в соответствии с заголовками Accept, отправленными в запросе. См. Согласование содержания .
- 407 Требуется проверка подлинности прокси (RFC 7235)
- Клиент должен сначала аутентифицироваться с помощью прокси .
- 408 Тайм-аут запроса
- Время ожидания запроса на сервере истекло. Согласно спецификациям HTTP: «Клиент не отправил запрос в течение времени, в течение которого сервер был подготовлен к ожиданию. Клиент МОЖЕТ повторить запрос без изменений в любое более позднее время».
- 409 Конфликт
- Указывает, что запрос не может быть обработан из-за конфликта в текущем состоянии ресурса, такого как конфликт редактирования между несколькими одновременными обновлениями.
- 410 ушел
- Указывает, что запрошенный ресурс больше не доступен и больше не будет доступен. Это следует использовать, когда ресурс был намеренно удален, и ресурс должен быть очищен. После получения кода состояния 410 клиент не должен запрашивать ресурс в будущем. Такие клиенты, как поисковые системы, должны удалить ресурс из своих индексов. В большинстве случаев не требуется, чтобы клиенты и поисковые системы очищали ресурс, и вместо этого можно использовать сообщение «404 Not Found».
- 411 Требуемая длина
- В запросе не указана длина его содержимого, необходимая для запрашиваемого ресурса.
- 412 Ошибка предварительного условия (RFC 7232)
- Сервер не соответствует одному из предварительных условий, которые запрашивающая сторона поставила в полях заголовка запроса.
- 413 Payload Too Large (RFC 7231).
- Запрос больше, чем сервер хочет или может обработать. Ранее назывался «Слишком большой объект запроса».
- 414 URI слишком длинный (RFC 7231)
- Предоставленный URI слишком длинный для обработки сервером. Часто это результат того, что слишком много данных кодируется как строка запроса запроса GET, и в этом случае его следует преобразовать в запрос POST. Ранее вызывался «Request-URI Too Long».
- 415 Неподдерживаемый тип носителя (RFC 7231)
- Объект запроса имеет тип мультимедиа, который сервер или ресурс не поддерживает. Например, клиент загружает изображение как image / svg + xml , но сервер требует, чтобы изображения использовали другой формат.
- 416 Диапазон не соответствует требованиям (RFC 7233)
- Клиент запросил часть файла ( обслуживание байтов ), но сервер не может предоставить эту часть. Например, если клиент запросил часть файла, лежащую за концом файла. Ранее называлась «Запрошенный диапазон не удовлетворяется».
- 417 Ожидание не выполнено
- Сервер не может удовлетворить требованиям поля заголовка запроса Expect.
- 418 Я чайник (RFC 2324, RFC 7168)
- Этот код был определен в 1998 году как одна из традиционных первоапрельских шуток IETF в RFC 2324, Hyper Text Coffee Pot Control Protocol , и не ожидается, что он будет реализован на реальных HTTP-серверах. RFC указывает, что этот код должен возвращаться чайниками, которых просят заварить кофе. Этот HTTP-статус используется в качестве пасхального яйца на некоторых веб-сайтах, например на Google.com « Я — пасхальное яйцо для чайника» .
- 421 неправильно направленный запрос (RFC 7540)
- Запрос был направлен на сервер, который не может дать ответ (например, из-за повторного использования соединения).
- 422 Unprocessable Entity (WebDAV; RFC 4918).
- Запрос был правильно сформирован, но его не удалось выполнить из-за семантических ошибок.
- 423 заблокировано (WebDAV; RFC 4918)
- Ресурс, к которому осуществляется доступ, заблокирован.
- 424 Неудачная зависимость (WebDAV; RFC 4918)
- Запрос не удался, потому что он зависел от другого запроса, и этот запрос не удался (например, PROPPATCH).
- 425 Слишком рано (RFC 8470)
- Указывает, что сервер не желает рисковать обработкой запроса, который может быть воспроизведен.
- 426 Требуется обновление
- Клиент должен переключиться на другой протокол, такой как TLS / 1.3 , указанный в поле заголовка Upgrade .
- 428 Требуется предварительное условие (RFC 6585)
- Исходный сервер требует, чтобы запрос был условным. Предназначен для предотвращения проблемы «потерянного обновления», когда клиент ПОЛУЧАЕТ состояние ресурса, изменяет его и отправляет обратно на сервер, когда тем временем третья сторона изменила состояние на сервере, что привело к конфликту.
- 429 Слишком много запросов (RFC 6585)
- Пользователь отправил слишком много запросов за заданный промежуток времени. Предназначен для использования со схемами ограничения скорости .
- 431 Слишком большие поля заголовка запроса (RFC 6585)
- Сервер не желает обрабатывать запрос, потому что либо отдельное поле заголовка, либо все поля заголовка в совокупности слишком велики.
- 451 недоступен по юридическим причинам (RFC 7725)
- Оператор сервера получил законное требование запретить доступ к ресурсу или набору ресурсов, который включает запрошенный ресурс. Код 451 был выбран в качестве ссылки на роман « 451 градус по Фаренгейту» (см. «Благодарности» в RFC).
5xx ошибки сервера
Сервер не смог выполнить запрос.
Коды состояния ответа, начинающиеся с цифры «5», указывают на случаи, когда сервер знает, что он обнаружил ошибку или иным образом не может выполнить запрос. За исключением ответа на запрос HEAD, сервер должен включать объект, содержащий объяснение ситуации с ошибкой, и указывать, является ли это временным или постоянным состоянием. Аналогичным образом, пользовательские агенты должны отображать пользователю любую включенную сущность. Эти коды ответов применимы к любому методу запроса.
- внутренняя ошибка сервера 500
- Общее сообщение об ошибке, которое выдается, когда возникла непредвиденная ситуация, и более конкретное сообщение не подходит.
- 501 Не реализовано
- Сервер либо не распознает метод запроса, либо не может выполнить запрос. Обычно это подразумевает доступность в будущем (например, новую функцию API веб-службы).
- 502 Неверный шлюз
- Сервер действовал как шлюз или прокси и получил недопустимый ответ от вышестоящего сервера.
- 503 Сервис недоступен
- Сервер не может обработать запрос (потому что он перегружен или отключен для обслуживания). Как правило, это временное состояние.
- Ошибка 504 Время ответа сервера истекло
- Сервер действовал как шлюз или прокси и не получил своевременного ответа от вышестоящего сервера.
- 505 Версия HTTP не поддерживается
- Сервер не поддерживает версию протокола HTTP, используемую в запросе.
- 506 вариант также согласовывается (RFC 2295)
- Прозрачное согласование содержимого для запроса приводит к циклической ссылке .
- 507 Недостаточно места для хранения (WebDAV; RFC 4918)
- Сервер не может сохранить представление, необходимое для выполнения запроса.
- Обнаружен цикл 508 (WebDAV; RFC 5842)
- Сервер обнаружил бесконечный цикл при обработке запроса (отправлено вместо 208 Already Reported ).
- 510 не расширенный (RFC 2774)
- Для его выполнения сервером требуются дальнейшие расширения запроса.
- 511 Требуется сетевая аутентификация (RFC 6585)
- Клиенту необходимо пройти аутентификацию, чтобы получить доступ к сети. Предназначен для использования путем перехвата прокси-серверов, используемых для управления доступом к сети (например, «перехватывающие порталы », используемые для запроса согласия с Условиями обслуживания перед предоставлением полного доступа в Интернет через точку доступа Wi-Fi ).
Неофициальные коды
Следующие ниже коды не определены никаким стандартом.
- 218 Это нормально ( веб-сервер Apache )
- Используется как условие универсальной ошибки, позволяющее передавать тела ответов через Apache, когда включен ProxyErrorOverride. Когда ProxyErrorOverride включен в Apache, тела ответов, содержащие код состояния 4xx или 5xx, автоматически отбрасываются Apache в пользу общего ответа или настраиваемого ответа, указанного в директиве ErrorDocument. Фраза «Все в порядке » — это интернет-мем, означающий игнорирование ситуации или бездействие, несмотря на очевидные доказательства продолжающейся катастрофы.
- 419 Страница истекла ( Laravel Framework )
- Используется Laravel Framework, когда токен CSRF отсутствует или просрочен.
- 420 Ошибка метода ( Spring Framework )
- Устаревший ответ, используемый Spring Framework в случае сбоя метода.
- 420 Повышайте свое спокойствие ( Twitter )
- Возвращается версией 1 Twitter Search and Trends API, когда клиент ограничен по скорости; версии 1.1 и более поздние используют код ответа 429 Too Many Requests . Фраза «Укрепи свое спокойствие» взята из фильма 1993 года « Человек-подрывник» , и ее связь с этим числом, вероятно, является отсылкой к каннабису .
- 430 Поля заголовка запроса слишком велики ( Shopify )
- Используется Shopify вместо кода ответа 429 Too Many Requests , когда слишком много URL-адресов запрашивается в течение определенного периода времени.
- 450 заблокировано родительским контролем Windows (Microsoft)
- Код расширения Microsoft указывает, когда родительский контроль Windows включен и блокирует доступ к запрошенной веб-странице.
- 498 Неверный токен (Esri)
- Возвращено ArcGIS for Server . Код 498 указывает на просроченный или недействительный токен по иным причинам.
- Требуется 499 токенов (Esri)
- Возвращено ArcGIS for Server . Код 499 указывает на то, что токен необходим, но не был отправлен.
- 509 Превышен предел пропускной способности ( веб-сервер Apache / cPanel )
- Сервер превысил пропускную способность, указанную администратором сервера; это часто используется провайдерами виртуального хостинга для ограничения пропускной способности клиентов.
- 529 Сайт перегружен
- Используется Qualys в сервере тестирования API SSLLabs , чтобы сигнализировать о том , что сайт не может обработать запрос.
- 530 Сайт заморожен
- Используется веб-платформой Pantheon для обозначения сайта, который был заблокирован из-за бездействия.
- 598 (Неофициальное соглашение) Ошибка тайм-аута сетевого чтения.
- Используется некоторыми прокси-серверами HTTP для сигнализации тайм-аута сетевого чтения за прокси-сервером клиенту перед прокси.
Информационные службы Интернета
Веб-сервер Microsoft Internet Information Services (IIS) расширяет пространство ошибок 4xx, чтобы сигнализировать об ошибках в запросе клиента.
- 440 Тайм-аут входа в систему
- Срок действия сеанса клиента истек, и ему необходимо снова войти в систему.
- 449 Повторить с
- Сервер не может выполнить запрос, потому что пользователь не предоставил требуемую информацию.
- 451 перенаправление
- Используется в Exchange ActiveSync, когда доступен более эффективный сервер или сервер не может получить доступ к почтовому ящику пользователей. Ожидается, что клиент повторно запустит операцию HTTP AutoDiscover, чтобы найти более подходящий сервер.
IIS иногда использует дополнительные десятичные субкоды для получения более конкретной информации, однако эти субкоды появляются только в полезных данных ответа и в документации, а не вместо фактического кода состояния HTTP.
nginx
Программное обеспечение веб-сервера nginx расширяет область ошибок 4xx, чтобы сигнализировать о проблемах с запросом клиента.
- 444 Нет ответа
- Используется внутри, чтобы указать серверу не возвращать информацию клиенту и немедленно закрыть соединение.
- 494 Заголовок запроса слишком большой
- Клиент отправил слишком большой запрос или слишком длинную строку заголовка.
- 495 Ошибка сертификата SSL
- Расширение кода ответа 400 Bad Request , используемого, когда клиент предоставил недействительный сертификат клиента .
- 496 Требуется сертификат SSL
- Расширение кода ответа 400 Bad Request , используемого, когда сертификат клиента требуется, но не предоставляется.
- 497 HTTP-запрос отправлен на HTTPS-порт
- Расширение кода ответа 400 Bad Request , используемого, когда клиент отправил HTTP-запрос на порт, который прослушивает HTTPS-запросы.
- 499 Клиент закрытый запрос
- Используется, когда клиент закрыл запрос до того, как сервер смог отправить ответ.
Cloudflare
Сервис обратного прокси Cloudflare расширяет область ошибок серии 5xx, чтобы сигнализировать о проблемах с исходным сервером.
- 520 Веб-сервер возвратил неизвестную ошибку
- Исходный сервер вернул Cloudflare пустой, неизвестный или неожиданный ответ.
- 521 Веб-сервер не работает
- Исходный сервер отказал в подключении Cloudflare. Решения безопасности в источнике могут блокировать законные соединения с определенных IP-адресов Cloudflare.
- 522 Время ожидания подключения истекло
- Время ожидания Cloudflare для связи с исходным сервером истекло.
- 523 Источник недоступен
- Cloudflare не смог связаться с исходным сервером; например, если записи DNS для исходного сервера неверны или отсутствуют.
- 524 Истекло время ожидания
- Cloudflare смог установить TCP-соединение с исходным сервером, но не получил своевременного ответа HTTP.
- 525 SSL Handshake Failed
- Cloudflare не удалось согласовать рукопожатие SSL / TLS с исходным сервером.
- 526 Неверный сертификат SSL
- Cloudflare не удалось проверить сертификат SSL на исходном веб-сервере. Также используется горутером Cloud Foundry .
- 527 Ошибка рельсотрона
- Ошибка 527 указывает на прерванное соединение между Cloudflare и сервером Railgun исходного сервера.
- 530
- Ошибка 530 возвращается вместе с ошибкой 1xxx.
AWS Elastic Load Balancer
Amazon «S Elastic Load Balancing добавляет несколько пользовательских кодов возврата
- 460
- Клиент закрыл соединение с балансировщиком нагрузки до истечения периода ожидания простоя. Обычно, когда время ожидания клиента превышает время ожидания Elastic Load Balancer.
- 463
- Балансировщик нагрузки получил заголовок запроса X-Forwarded-For с более чем 30 IP-адресами.
- 561 Неавторизованный
- Ошибка аутентификации, возвращенная сервером, зарегистрированным в балансировщике нагрузки. Вы настроили правило прослушивателя для аутентификации пользователей, но поставщик удостоверений (IdP) вернул код ошибки при аутентификации пользователя.
Кэширование кодов предупреждений
Следующие коды предупреждений, связанные с кешированием , указаны в RFC 7234. В отличие от других кодов состояния, приведенных выше, они отправляются не как статус ответа в протоколе HTTP, а как часть HTTP-заголовка «Предупреждение». Поскольку этот заголовок часто не пересылается серверами и не подтверждается клиентами, он скоро будет отменен Рабочей группой HTTP .
- 110 Ответ устарел
- Ответ, предоставленный кешем, устарел (возраст содержимого превышает максимальный возраст, установленный заголовком Cache-Control или эвристически выбранным временем жизни).
- 111 Ошибка повторной валидации
- Кэш не смог проверить ответ из-за невозможности связаться с исходным сервером.
- 112 Работа в отключенном состоянии
- Кэш намеренно отключен от остальной сети.
- 113 Эвристическое истечение
- Кэш эвристически выбрал срок действия обновления более 24 часов, а возраст ответа превышает 24 часа.
- 199 Прочие предупреждения
- Произвольное, неспецифическое предупреждение. Текст предупреждения может быть зарегистрирован или предоставлен пользователю.
- 214 Преобразование применено
- Добавляется прокси-сервером, если он применяет к представлению какие-либо преобразования, такие как изменение кодировки контента, типа мультимедиа и т.п.
- 299 Разное постоянное предупреждение
- То же, что и 199, но с постоянным предупреждением.
Смотрите также
- Пользовательские страницы ошибок
- Список кодов возврата FTP-сервера
- Список полей заголовка HTTP
- Список кодов возврата SMTP-сервера
- Общий формат журнала
Примечания
использованная литература
внешние ссылки
- RFC 7231 — Протокол передачи гипертекста (HTTP / 1.1): семантика и контент — раздел 6, коды состояния ответа
- Реестр кода состояния протокола передачи гипертекста (HTTP)
- База знаний Microsoft: MSKB943891: коды состояния HTTP в IIS 7.0
- База знаний Microsoft Office: код ошибки 2–11
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее… |
Содержание
- 1 Многопоточность в Java
- 1.1 Программирование многопоточного сокета в Java
- 1.2 Многопоточная программа Socket сервера в Java
- 1.3 Клиентская программа сервера
- 1.4 Клиентская программа
- 1.5 Как запустить эту программу?
Все разработчики знакомы с написанием последовательных программ, каждая последовательная программа имеет начало, последовательность выполнения и конец. Поток — это один последовательный поток управления внутри программы. Это независимый путь выполнения программного кода. Большинство программ, написанных сегодня, выполняются как один поток, что вызывает проблемы при одновременном возникновении нескольких событий или действий. Когда выполняется несколько потоков, путь одного потока через один и тот же код обычно отличается от других. Каждый поток в Java создается и контролируется классом Java.lang.Thread.
Существует два способа создания потока в Java;
- Внедрить интерфейс Runnable (Java.lang.Runnable)
- Расширением класса Thread (Java.lang.Thread)
Многопоточность в Java
Многопоточность в Java — это процесс одновременного выполнения нескольких потоков. Многопоточная программа содержит два или более процесса, которые могут выполняться одновременно, и каждый процесс может обрабатывать другую задачу, одновременно обеспечивая оптимальное использование доступных ресурсов, особенно когда ваш компьютер имеет несколько процессоров. Процесс выполнения нескольких потоков одновременно известен как многопоточность.
Программирование многопоточного сокета в Java
В предыдущем примере мы уже видели, как Single Socket Socket Program . В этом случае только один клиент может связываться с сервером. Он не позволит одновременных подключений клиентов. Попробуйте запустить другого клиента. Вы увидите, что второй клиент не может быть подключен, пока первый клиент не закроет его соединение. Чтобы разрешить одновременные соединения, мы должны знать многопоточное программирование. Здесь, в следующем многопоточном программировании сокета, вы можете подключить более одного клиента к серверу и обмениваться данными.
Для каждого клиентского соединения сервер запускает дочерний поток для обработки запроса независимо от любых других входящих запросов.
Socket serverClient=server.accept(); ServerClientThread sct = new ServerClientThread(serverClient,counter);
ServerClientThread — это новый класс, который расширяет класс Thread. Здесь вы можете видеть, а не обрабатывать входящие запросы в том же потоке, который принимает клиентское соединение, соединение передается клиентскому потоку, который обрабатывает запрос. Таким образом, поток, прослушивающий следующие входящие запросы, тратит как можно больше времени на вызов serverSocket.accept(). Таким образом, риск сводится к минимуму, поскольку клиентам отказывают в доступе к серверу, потому что поток прослушивания не находится внутри вызова accept(). Здесь клиентский поток фактически выполняет запрос. Тем временем сервер может принимать несколько клиентских запросов и запускать обработку. Поэтому отдельные потоки будут запущены, и они будут работать параллельно. В этом примере клиент отправляет номер на сервер и в ответ на каждый клиент сервер отправляет квадрат полученного номера.
Многопоточная программа Socket сервера в Java
import Java.net.*; import Java.io.*; public class MultithreadedSocketServer { public static void main(String[] args) throws Exception { try{ ServerSocket server=new ServerSocket(8888); int counter=0; System.out.println("Server Started ...."); while(true){ counter++; Socket serverClient=server.accept(); // сервер принимает запрос на подключение клиента System.out.println(" >> " + "Client No:" + counter + " started!"); ServerClientThread sct = new ServerClientThread(serverClient,counter); // отправляем запрос в отдельный поток sct.start(); } }catch(Exception e){ System.out.println(e); } } }
Клиентская программа сервера
Этот класс потока клиентов сервера обрабатывал запрос независимо от любых других входящих запросов. Следующая программа Java является частью программы Multithreaded Server Socket.
class ServerClientThread extends Thread { Socket serverClient; int clientNo; int squre; ServerClientThread(Socket inSocket,int counter){ serverClient = inSocket; clientNo=counter; } public void run(){ try{ DataInputStream inStream = new DataInputStream(serverClient.getInputStream()); DataOutputStream outStream = new DataOutputStream(serverClient.getOutputStream()); String clientMessage="", serverMessage=""; while(!clientMessage.equals("bye")){ clientMessage=inStream.readUTF(); System.out.println("From Client-" +clientNo+ ": Number is :"+clientMessage); squre = Integer.parseInt(clientMessage) * Integer.parseInt(clientMessage); serverMessage="From Server to Client-" + clientNo + " Square of " + clientMessage + " is " +squre; outStream.writeUTF(serverMessage); outStream.flush(); } inStream.close(); outStream.close(); serverClient.close(); }catch(Exception ex){ System.out.println(ex); }finally{ System.out.println("Client -" + clientNo + " exit!! "); } } }
Клиентская программа
Это настоящая клиентская программа, запрашивающая подключение к серверу. Для каждого клиента вам нужно открыть отдельное окно консоли для запуска клиентской программы.
import Java.net.*; import Java.io.*; public class TCPClient { public static void main(String[] args) throws Exception { try{ Socket socket=new Socket("127.0.0.1",8888); DataInputStream inStream=new DataInputStream(socket.getInputStream()); DataOutputStream outStream=new DataOutputStream(socket.getOutputStream()); BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String clientMessage="",serverMessage=""; while(!clientMessage.equals("bye")){ System.out.println("Enter number :"); clientMessage=br.readLine(); outStream.writeUTF(clientMessage); outStream.flush(); serverMessage=inStream.readUTF(); System.out.println(serverMessage); } outStream.close(); outStream.close(); socket.close(); }catch(Exception e){ System.out.println(e); } } }
Как запустить эту программу?
Следующим шагом является запуск программы Java TCPClient Socket Program на том же компьютере или других компьютерах в той же сети. Когда вы запускаете клиентскую программу, она установит соединение с сервером и ожидает ввода с клиентской стороны. Клиентская программа неоднократно запрашивает у пользователя ввод целого числа, отправляет на сервер и получает квадрат целого с сервера. Если вы хотите протестировать несколько клиентов, для каждого клиента вам нужно открыть отдельное окно консоли для запуска клиентской программы. Когда клиент отправляет «bye» со стороны клиента, сервер закрывает соединение с клиентом. На следующем изображении вы можете увидеть, как сервер и несколько клиентов обмениваются данными с сервером.
Если ваша программа «Сервер и клиент» работает на одном компьютере, дайте «127.0.0.1».
Socket socket=new Socket("127.0.0.1",8888);
В противном случае укажите IP-адрес устройства, на котором запущен MultithreadedSocketServer.
Источник: http://net-informations.com/Java/net/multithreaded.htm
Пишу многопоточный сервер и почему то не могу получить ответ от сервера в консоль . В качестве клиента использую curl -X POST -d «temperature=18» http://localhost:9000/queue/weather
Вроде бы данные попадают в out но не получаю ответа от сервера . Все переменные правильные смотрел в дебаге. В простом сервере без пула все отрабатывает верно
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
private final HashMap<String, Service> modes = new HashMap<>();
public void start() {
modes.put("queue", new QueueService());
modes.put("topic", new TopicService());
ExecutorService pool = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
try (ServerSocket server = new ServerSocket(9000)) {
while (!server.isClosed()) {
Socket socket = server.accept();
pool.execute(() -> {
try (OutputStream out = socket.getOutputStream();
InputStream input = socket.getInputStream()) {
byte[] buff = new byte[1_000_000];
var total = input.read(buff);
var text = new String(Arrays.copyOfRange(buff, 0, total), StandardCharsets.UTF_8);
var req = Req.of(text);
var resp = modes.get(req.method()).process(req);
var temp = ("HTTP/1.1 " + resp.status() + " OKrn");
out.write(temp.getBytes());
var temp2 = resp.text();
out.write(temp2.getBytes());
out.flush();
System.out.println("hi");
} catch (IOException e) {
e.printStackTrace();
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Server().start();
}
}
public class Req {
private final String method;
private final String mode;
private final String text;
private Req(String method, String mode, String text) {
this.method = method;
this.mode = mode;
this.text = text;
}
public static Req of(String content) {
/* TODO parse a content */
String[] array = content.split("rn");
String[] temp = array[0].split("/");
String tempMethod = temp[1];
String[] temp2 = temp[2].split(" ");
String tempMode = temp2[0];
return new Req(tempMethod, tempMode, array[7]);
}
public String method() {
return method;
}
public String mode() {
return mode;
}
public String text() {
return text;
}
}
public class Resp {
private final String text;
private final int status;
public Resp(String text, int status) {
this.text = text;
this.status = status;
}
public String text() {
return text;
}
public int status() {
return status;
}
}
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
public class QueueService implements Service {
private ConcurrentHashMap<String, ConcurrentLinkedQueue<String>> queue = new ConcurrentHashMap<>();
private AtomicInteger id = new AtomicInteger();
@Override
public Resp process(Req req) {
queue.putIfAbsent(req.mode(), new ConcurrentLinkedQueue<>());
queue.get(req.mode()).add(req.text());
var result = queue.get(req.mode()).poll();
return new Resp(result, id.incrementAndGet());
}
}
задан 15 июн 2021 в 6:24
TerasanTerasan
1591 золотой знак1 серебряный знак10 бронзовых знаков
4
У вас неправильно реализован протокол HTTP. Из ответа HTTP должен быть способ узнать его длину.
Нужно либо вернуть заголовок Content-Length
— в этом случае клиент закончит обработку запроса по получению указаного количества байт.
Либо нужно вернуть заголовок Connection: close
, тогда клиент определяет конец сообщения по закрытому соединению.
Кроме того, между заголовками и телом должны идти переводы строки.
Вот так вот приблизительно:
Resp resp = modes.get(req.method()).process(req);
String temp = ("HTTP/1.1 " + resp.status() + " OKn");
out.write(temp.getBytes());
String header = "Connection: close";
out.write(header.getBytes(StandardCharsets.UTF_8));
out.write("rnrn".getBytes());
String temp2 = resp.text();
out.write(temp2.getBytes());
out.flush();
ответ дан 15 июн 2021 в 8:08
3