Отличная тема для разработки современных приложений — переписывание. С момента появления 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, и его просто можно включить! Возможно, стоит подумать дважды, прежде чем внедрять здесь.