С выходом Java EE 6 на ряду со значительными изменениями в JPA 2.0 спецификации сервлетов 3.0 также притерпела ряд улучшений: упростилась разработка и процедура развертывания, конфигурирование стало более удобным, появилась поддержка асинхронных запросов и улучшилась модель безопасности. Далее я попытаюсь осветить основные изменения в API.
Программирование и развертывание сервлетов упростилось главным образом за счет введения аннотаций для декларирования сервлет (@WebServlet), фильтров (@WebFilter), листнеров (@WebListener) и ограничений безопасности (@ServletSecurity). Таким образом, и дескриптор развертывания web.xml стал опциональным элементом. Обращаю внимание, что сами компоненты Servlet API не стали POJO, привычную иерархию интерфейсов и классов никто не отменял. Также добавилась аннотация для поддержки загрузки файлов @MultipartConfig и для установки параметров инициализации @WebInitParam.
Пример Hello World сервлета
package net.ismailov.tests;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name="hw", urlPatterns = "/hello_world")
public class HelloWorld extends HttpServlet{
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter writer = response.getWriter();
writer.println("<h1>Hello, World!</h1>");
}
}
Хорошей новостью стало появление возможности динамической регистрации сервлетов:
ServletRegistration.Dynamic dynamic =
servletContext.addServlet(“DynamicTestServlet”, “net.ismailov.DynamicTestServlet”)
dynamic.addMapping(“/dynamicServlet”);
Для поддержки длительных операций добавилась возможность асинхронной работы сервлета. Для включения данной возможности необходимо:
//Либо декларативно указать поддержку асинхронного режима
@WebServlet(asyncSupported=true)
//Либо установить при динамической инициализации сервлета
dynamic.setAsyncSupported(true);
После этого response не перестает существовать по завершении метода. Необходимо вызвать
AsyncContext ctx = ServletRequest.startAsync(req, res)
Теперь экземляры request и response будут сохранены, и, по завершении выполнения асинхронного метода, могут быть использованы для вывода пользователю одним из методов AsyncContext.dispatch(/*различные параметры*/)
@WebServlet("/MyAsyncTestServlet" asyncSupported=true)
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) {
...
AsyncContext aCtx = request.startAsync(req, res);
ScheduledThreadPoolExecutor executor = new ThreadPoolExecutor(10);
executor.execute(new MyAsyncService(aCtx));
}
}
public class MyAsyncService implements Runnable {
private AsyncContext ctx;
public MyAsyncService(AsyncContext ctx) {
this.ctx = ctx;
}
public void run() {
// Может быть вызвана долгая операция с последующим выводом
ctx.dispatch("/result.jsp");
}
Как уже упоминалось, с новым API стало возможно задавать ограничения доступа, например:
@WebServlet(name="hw", urlPatterns = "/hello_world")
@ServletSecurity(@HttpConstraint(rolesAllowed = {"admin"}))
public class HelloWorld extends HttpServlet{
Естественно, задача задания ролей/пользователей, равно как и аутентификация, являются vendor-specific. К примеру, glassfish выдает basic http auth формочку:
Также имеется возможность накладывания ограничений на методы доступа:
@ServletSecurity(httpMethodConstraints={ @HttpMethodConstraint("GET"),
@HttpMethodConstraint(value="POST", rolesAllowed={"user_group_1"})})
Основные нововведения в api 3.0 я постарался отразить, осталось лишь отметить, что с декабря прошлого года доступна «эталонная реализация» спецификации Java EE6: Glassfish 3.0, в которой можно поэкспериментировать с новым API.