Статьи

Переписать до края — получить максимальную отдачу от этого! На GlassFish!

Отличная тема для разработки современных приложений — переписывание. С момента появления Java Server Faces и новой облегченной модели программирования в Java EE 6 вы боретесь с красивыми и простыми, доступными для закладки URL-адресами. PrettyFaces был там некоторое время, и даже если бы его можно было назвать зрелым в версии 3.3.3, я не был убежден.

Главным образом из-за того, что мне пришлось настроить его в xml. Если вы когда-либо делали проект JSF, вы знаете, что это то, что вы делаете позже. Или никогда. С последним вариантом, который я видел много.
Переписать собирается изменить это. Программный, простой в использовании и настраиваемый. Именно то, что я искал.

Начало работы

Нет ничего проще, чем начать работу с материалами одного из ребят из RedHat. Запустите NetBeans, создайте новое веб-приложение на основе Maven, добавьте JSF и Primefaces к миксу и запустите его на GlassFish.

Первый шаг для добавления магии переписывания в ваше приложение — это добавление зависимостей перезаписи в ваш проект.

 

<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-servlet</artifactId>
    <version>1.1.0.Final</version>
</dependency>

Этого недостаточно, так как я собираюсь использовать его вместе с JSF, вам также необходима интеграция с jsf.

<dependency>
          <groupId>org.ocpsoft.rewrite</groupId>
          <artifactId>rewrite-integration-faces</artifactId>
          <version>1.1.0.Final</version>
      </dependency>

Затем реализуйте свой собственный ConfigurationProvider. Это центральная часть, где происходит большая часть магии. Давайте пока назовем это TricksProvider, и мы также расширим абстрактный HttpConfigurationProvider. Простая первая версия выглядит так:

public class TricksProvider  extends HttpConfigurationProvider
{
   @Override
   public int priority()
   {
     return 10;
   }
 
   @Override
   public Configuration getConfiguration(final ServletContext context)
   {
     return ConfigurationBuilder.begin()
        .addRule(Join.path("/").to("/welcomePrimefaces.xhtml"));
    }
}

Теперь вы должны зарегистрировать свой ConfigurationProvider. Вы делаете это, добавляя простой текстовый файл с именем org.ocpsoft.rewrite.config.ConfigurationProvider в папку вашего приложения / META-INF / services /. Добавьте к нему полное имя вашей реализации ConfigurationProvider, и все готово. Если вы запустите свое приложение.

Основы переписывания
При копировании вышеуказанного провайдера вы неявно добавили свое первое правило перезаписи. Запрашивая http: // host: 8080 / yourapp /, вы попадаете на страницу приветствия Primefaces, созданную NetBeans. Все правила основаны на одном и том же принципе. Каждое правило состоит из условия и операции. Что-то вроде «Если X случится, сделай Y». Переписать знает два разных вида правил. Некоторые предварительно сконфигурированные (Join), начинающиеся с addRule (), и свободный интерфейс, начинающийся с defineRule (). Это немного сбивает с толку, потому что следующий основной выпуск устареет defineRule () и переименует его в addRule (). Так что большинство примеров, которые вы найдете (особенно тестовые примеры в последней транке), не работают с 1.1.0.Final.
Переписать знает о двух разных направлениях. Входящий и исходящий. Входящий, скорее всего, работает как любой известный вам механизм перезаписи (например, mod_rewrite). Запрос поступает и перенаправляется или перенаправляется на ресурсы, определенные в ваших правилах. Исходящее направление немного меньше. Он в основном имеет хук в методе encodeURL () HttpServletRequest и переписывает ссылки, которые есть на ваших страницах (если они вообще отображаются с помощью encodeURL). JSF делает это из коробки. Если вы планируете использовать его с JSP, вы должны сами его назвать.

Переадресация .html в .xhtml с некоторой магией
Давайте рассмотрим некоторые вещи, которые вы могли бы сделать с помощью перезаписи. Сначала мы добавляем в TricksProvider следующее:

.defineRule()
.when(Direction.isInbound()
.and(Path.matches("{name}.html").where("name").matches("[a-zA-Z/]+")))
.perform(Forward.to("{name}.xhtml"));

Это правило, которое просматривает входящие запросы и проверяет все совпадения Patch {name} .html, которые подтверждают шаблон регулярного выражения [a-zA-Z /] +, и перенаправляет их в файлы {name} .xhtml.
Если это правило действует, все запросы к http: // host: 8080 / yourapp / something.html будут в конечном итоге перенаправлены в нечто .xhtml. Теперь ваши пользователи больше не будут знать, что вы используете причудливые JSF-материалы, и верят, что вы работаете с html ? Если запрашивается URL, не соответствующий регулярному выражению, например что-то вроде http: // host: 8080 / yourapp /thing123.html это просто не пересылается, и если что-то12123.html отсутствует в вашем приложении, вы в конечном итоге получите ошибку 404.

Перезапись исходящих ссылок
. Вы также можете добавить следующее правило:

.defineRule()
.when(Path.matches("test.xhtml")
.and(Direction.isOutbound()))
.perform(Substitute.with("test.html"))

Вы представляете, что это делает, верно? Если у вас есть лицевая сторона, которая содержит что-то вроде этого: 

<h:outputLink value="test.xhtml">Normal Test</h:outputLink>

Ссылка, которая отображается для пользователя, будет переписана в test.html. Это самое основное действие для исходящих ссылок, которое вам когда-либо понадобится. Большая часть магии происходит с входящими ссылками. Неудивительно, если взглянуть на очень ограниченный охват хука encodeURL ().

OutputBuffer
Самый удивительный материал в перезаписи называется OutputBuffer. По крайней мере, до релиза, с которым мы сейчас работаем. Он будет переименован в 2.0, но сейчас давайте просто посмотрим, что вы можете сделать. OutputBuffer — ваш хук к ответу. Все, что вы хотели бы сделать с ответом до его фактического поступления в браузер вашего клиента, можно сделать здесь. Думаете о преобразовании разметки? Преобразование CSS? Или даже сжатие GZIP? Отлично, это именно то, что вы могли бы сделать. Позволять’реализовать простой ZipOutputBuffer

public class ZipOutputBuffer implements OutputBuffer {
 
    private final static Logger LOGGER = Logger.getLogger(ZipOutputBuffer.class.getName());
 
    @Override
    public InputStream execute(InputStream input) {
        String contents = Streams.toString(input);
        LOGGER.log(Level.FINER, "Content {0} Length {1}", new Object[]{contents, contents.getBytes().length});
        byte[] compressed = compress(contents);
        LOGGER.log(Level.FINER, "Length: {0}", compressed.length);
        return new ByteArrayInputStream(compressed);
    }
 
    public static byte[] compress(String string) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(string.length());
        byte[] compressed = null;
        try {
            try (GZIPOutputStream gos = new GZIPOutputStream(os)) {
                gos.write(string.getBytes());
            }
            compressed = os.toByteArray();
            os.close();
        } catch (IOException iox) {
            LOGGER.log(Level.SEVERE, "Compression Failed: ", iox);
        }
        return compressed;
    }
}

Как вы можете видеть, я возиться с некоторыми потоками и использую java.util.zip.GZIPOutputStream, чтобы уменьшить поток, полученный этим методом. Далее мы должны добавить соответствующее правило в TricksProvider:

.defineRule()
.when(Path.matches("/gziptest").and(Direction.isInbound()))
.perform(Forward.to("test.xhtml")
.and(Response.withOutputBufferedBy(new ZipOutputBuffer())
.and(Response.addHeader("Content-Encoding", "gzip"))
.and(Response.addHeader("Content-Type", "text/html"))))

Входящее правило (мы не хотим переписывать ссылки на страницах здесь … поэтому оно должно быть входящим), которое добавляет ZipOutputBuffer к Ответу. Также позаботьтесь о дополнительном заголовке ответа (оба), если вы не хотите, чтобы ваш браузер жаловался на содержимое, которое я перепутал ? Вот и все. Запрос http: // host: 8080 / yourapp / gziptest теперь доставляет test.xhtml со сжатием GZIP. Это 2,6 КБ против 1,23 КБ! Менее половины размера! Не очень удобно работать с потоками и байтами []. И я не уверен, будет ли это работать с большими размерами страниц с точки зрения фрагментации памяти, но это простой выход, если у вас нет фильтра сжатия или вам нужно только сжимать отдельные части вашего приложения.

Повысить безопасность с помощью Rewrite
Но это еще не все, что вы могли бы сделать: вы также можете повысить безопасность с помощью перезаписи. У Линкольна есть отличный пост о защите вашего приложения с помощью перезаписи . Есть много возможных примеров того, как это использовать. Я придумал один вариант использования, в котором не хотел использовать функции файла приветствия и предпочитаю отправлять пользователей индивидуально. При этом я также проверял их пути и проверял, являются ли вводимые ими данные вредоносными или нет. Вы можете сделать это либо с условием .matches (), либо с пользовательским ограничением. Добавьте следующее в TricksProvider:

Constraint<String> selectedCharacters = new Constraint<String>() {
        @Override
        public boolean isSatisfiedBy(Rewrite event,
                EvaluationContext context, String value) {
            return value.matches("[a-zA-Z/]+");
        }
    };

И определите следующее правило: 

.defineRule()
.when(Direction.isInbound()
.and(Path.matches("{path}").where("path").matches("^(.+)/$")
.and(Path.captureIn("checkChar").where("checkChar").constrainedBy(selectedCharacters))))
.perform(Redirect.permanent(context.getContextPath() + "{path}index.html"))

Еще одна входящая модификация. Проверка пути, если он имеет шаблон папки, и запись его в переменную, которая проверяется на соответствие пользовательским ограничениям. Большой! Теперь у вас есть простой и удобный механизм пересылки. Все http: // host: 8080 / yourapp / folder / request теперь переписываются на http: // host: 8080 / yourapp / index.html. Если вы посмотрите на другие правила сверху, вы увидите, что .html перенаправляется в .xhtml … и все готово!

Итог
Мне очень нравится работать с переписать много. Это проще, чем настраивать xml-файлы prettyfaces, и мне действительно понравилась поддержка Линкольна и Кристиана.во время моих первых шагов с ним. Мне любопытно посмотреть, что выйдет с 2.0, и я надеюсь, что я получу еще один отладочный вывод для конфигурации правил, просто чтобы посмотреть, что происходит. По умолчанию ничего нет, и может быть очень сложно найти правильную комбинацию условий, чтобы иметь рабочее правило.
Ищете полные источники? Найди их на github . Рад прочитать о вашем опыте.

Где находится GlassFish Part?
О да. Я упоминал об этом в заголовке, верно? Это должно быть больше похоже на дефолт. Я запускал все с последней версией GlassFish 3.1.2.2, так что вы можете быть уверены, что это работает. И NetBeans на 7,2на данный момент, и вы должны попробовать, если у вас нет. Я не сталкивался ни с одной проблемой, связанной со GlassFish, и мне очень приятно подчеркнуть это здесь. Отличная работа! И последнее замечание: прежде чем вы собираетесь безумно реализовать OutputBuffer, взгляните на то, что ваш любимый сервер приложений уже есть в наличии. GlassFish уже знает о сжатии GZIP, и его просто можно включить! Возможно, стоит подумать дважды, прежде чем внедрять здесь.