Статьи

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

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

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

Начиная

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

1
2
3
4
5
<dependency>
    <groupId>org.ocpsoft.rewrite</groupId>
    <artifactId>rewrite-servlet</artifactId>
    <version>1.1.0.Final</version>
</dependency>

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

1
2
3
4
5
<dependency>
          <groupId>org.ocpsoft.rewrite</groupId>
          <artifactId>rewrite-integration-faces</artifactId>
          <version>1.1.0.Final</version>
      </dependency>

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
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 следующее:

1
2
3
4
.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.

Перезапись исходящих ссылок

В противном случае вы также можете добавить следующее правило:

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

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

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

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

OutputBuffer

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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:

1
2
3
4
5
6
.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:

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

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

1
2
3
4
5
.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, и его просто можно включить! Возможно, стоит подумать дважды, прежде чем внедрять здесь.

Ссылка: Переписать до краев — получить максимальную отдачу от этого! На GlassFish! от нашего партнера по JCG Маркуса Эйзела (Markus Eisele) из блога « Разработка программного обеспечения для предприятий с использованием Java» .