Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Servlet 2.4: Что ожидать?

Автор:Джейсон Хантер
Перевод на русский c Дмитрий Зацеляпин, 2003

Обзор

7 марта 2003 Sun Microsystems выпустил последний релиз "Proposed Final Draft 2" спецификации Servlet 2.4. Эта статья объясняет различия между Servlet 2.3 и 2.4, рассказывает о причинах изменений, и показывает, как вы можете использовать преимущества новых возможностей в 2.4. (В оригинальной версии 3,100 слов)

7 марта, 2003, Sun Microsystems, работая совместно со 154 группами экспертов JSR (Java Specification Request), опубликовал "Proposed Final Draft 2" спецификации Servlet 2.4 (см. ссылку на официальную спецификацию Ресурсы ). Проект не завершен, и технические детали могут меняться. Еще не определены окончательные сроки Proposed Final Draft 3. Однако, изменения, которые будут внесены в окончательный релиз, не должны быть значительными, и, фактически, разработчики серверов уже начали реализацию новых возможностей. Это значит, что сейчас самое время начать их изучение, и проверить интеграцию Servlet 2.4 c J2EE (Java 2 Platform, Enterprise Edition) 1.4.

В этой статье описывается, что изменилось в версии 2.4 по сравнению с 2.3, объясняются причины принятых решений, а также рассказывается о некоторых моментах, которые так и не были реализованы. Для того, что бы сохранить направленность статьи, я предполагаю, что вы знакомы с классами и методами предыдущих версий Servlet API . Если нет, вы можете обратиться к ссылкам на те сайты (и к моей книге), которые помогут вам быстро набрать скорость — ресурсы.

Servlet 2.4 не поражает революционными изменениями, как прошлые релизы. Servlet 2.2 ввел возможность независимых Web приложений. Servlet 2.3 добавил мощь фильтров и цепочек фильтров. Servlet 2.4, хотя и добавляет несколько интересных возможностей, главным образом, направлен на связывание несвязанных концов, доводку и прояснение уже введенных ранее возможностей. Эффект этой работы в том, что сервера, полностью реализующие 2.4, будут более жизнеспособными, чем любые старые сервера. Но не думайте, что в Servlet 2.4 нет ничего нового! Вот список новых возможностей:

  • Сервлетам необходима поддержка HTTP/1.1 и J2SE (Java 2 Platform, Standard Edition) 1.3, и они могут работать с J2EE 1.4
  • В ServletRequest добавлены новые методы для контроля за соединениями клиентов
  • Новая поддержка интернационализации и выбора набора символов
  • Добавлены новые и доработаны некоторые старые возможности RequestDispatcher
  • Добавлены новые методы и классы слушателей ServletRequest
  • Объявлена не рекомендованной к использованию(deprecated) SingleThreadModel
  • Доработаны возможности аутентификации с помощью HttpSession
  • Доработана загрузка классов и поведение файлов welcome
  • Добавлено большое количество новых элементов в web.xml, который теперь использует XML схему.

Перед тем как начать рассматривать эти изменения, напомню, что большинство серверов еще не полностью совместимы с реализацией Servlet 2.4. Если вы хотите протестировать новые возможности, лучше всего скачать официальную реализацию сервера Apache Tomcat 5.0. Это — открытый исходный код, вы можете загрузить его бесплатно. Tomcat 5.0 будет первой версией Tomcat, поддерживающей Servlet 2.4, но, конечно, это только альфа релиз. (см информацию по Tomcat см. Ресурсы )

Модернизация поддержки HTTP, J2SE, и J2EE

Servlet 2.4 зависит от HTTP/1.1 и J2SE 1.3. Раньше сервлеты основывались на HTTP/1.0 и J2SE 1.2. С момента появления 2.4 новые минимальные требования дают возможность авторам сервлетов использоваться HTTP/1.1 и J2SE 1.3. В то же время, эти требования усложняют задачу разработчиков контейнеров сервлетов, потому что HTTP/1.1 имеет больше особенностей и сложностей, чем HTTP/1.0. Некоторые сервера уже поддерживают HTTP/1.1, их разработчикам не понадобится много времени на модернизацию. Заметьте, что те из них, кто считают, что спецификацию Servlet 2.4, можно реализовать на технологии J2SE 1.2, принимают не самое лучшее решение. Так как J2SE 1.3 является минимальным требованием, это разорвет соглашение с авторами сервлетов, соглашение, которое гласит, что автор может использовать возможности J2SE 1.3, когда пишет на Servlet 2.4.

Как другое изменение можно расценивать тот факт, что Servlet 2.4 будет частью ожидаемого J2EE 1.4 (фактически, две спецификации будут выпущены одновременно, вероятно, к JavaOne 2003). Конечно, важно отметить, что сервлеты могут работать самостоятельно. Не обязательно покупать полный контейнер J2EE для запуска сервлетов. Apache Tomcat, например, не реализует весь J2EE. Однако, когда вы запускаете сервлеты как часть J2EE 1.4, вы получаете преимущество в виде дополнительных возможностей, такие как новые элементы дескриптора загрузки, использование ресурсов JNDI, EJB, очереди сообщений, сервисы JAX-RPC. Я скажу об этом подробнее позднее.

Модернизация до HTTP/1.1 стала еще одним изменением. В сервлетах появляется дополнительная статическая константа HttpServletResponse.SC_FOUND для представления 302 кода статуса запроса. Найден в HTTP/1.1 означает то, что в HTTP/1.0 означал Временно перемещен. HttpServletResponse.SC_MOVED_TEMPORARILY еще существует и представляет код 302, но SC_FOUND является предпочтительным. SC_MOVED_TEMPORARILY может считаться устаревшим, но устаревшие переменные, даже если это константы с общим доступом в Java технически невозможны.

Новые методы ServletRequest

Интерфейсы ServletRequest и ServletRequestWrapper пополняются четырьмя новыми методами:

  • getRemotePort(): Возвращает IP порт клиента или последнего proxy, который отправлял запрос
  • getLocalName(): Возвращает имя хоста интерфейса IP на который был принят запрос
  • getLocalAddr(): Возвращает IP адрес интерфейса на который был принят запрос
  • getLocalPort(): Возврашает номер IP порта интерфейса на который был принят запрос

Эти методы дают возможность получать информацию о состоянии IP соединения на нижнем уровне и узнавать, как происходит маршрутизация соединения. Метод getRemotePort() в комбинации с уже существовавшими методами getRemoteAddr() и getRemoteHost(), выдает информацию о клиентской стороне IP соединений. Методы getLocalPort(), getLocalAddr(), и getLocalPort() выдают информацию о серверной стороне IP соединений. Существующие в предыдущей версии методы getServerName() и getServerPort() были заново определены для того, чтобы выдавать информацию об уровне HTTP простым возвратом пары "host:port", получаемой из заголовка HTTP. На виртуальных хостах и системах с распределенной нагрузкой эти методы дают возможность узнать, как клиенты, прокси сервера, или устройства распределения нагрузки связываются, как они соединены физически и логически.

Интернационализация

В Servlet 2.4 интерфейс ServletResponse и ServletResponseWrapper дополнены двумя методами:

  • setCharacterEncoding(String encoding): устанавливает кодировку символов ответа. Этот метод предоставляет альтернативу методу setContentType(String), получающему имя набора символов(charset) в качестве параметра и методу setLocale(Locale), в который передается объект Locale. Этот метод не имеет эффекта, если вызывается после getWriter(), или если ответ уже был проведен (committed). Список доступных наборов символов, см. ресурсы.
  • getContentType(): возвращает тип содержимого (content type) ответа. Он может содержать значение набора символов(charset), установленное с помощью setContentType(), setLocale(), или setCharacterEncoding(). В случае, если тип содержимого не был указан, метод возвращает null.

Новый метод setCharacterEncoding() сочетается с существовавшим ранее getCharacterEncoding() для более легкого изменения и просмотра набора символов (charset). Теперь можно избежать неуклюжего метода установки набора символов путем вызова setContentType("text/html; charset=UTF-8").

Новый метод getContentType() работает совместно с существовавшим ранее методом setContentType() и предназначен для получения установлено значения типа содержимого (content type). Раньше в этом не было большой необходимости, но сейчас тип содержимого может быть установлен комбинацией вызовов setContentType(), setLocale(), и setCharacterEncoding(), и этот метод дает возможность видеть тип сгенерированной строки.

Так что же лучше, setLocale() или setCharacterEncoding()? Это зависит от ситуации. Старый метод позволяет установить Locale, задав к примеру ja для японского и позволить контейнеру производить определение подходящего набора символов. Это удобно, но, для данной Locale может быть много наборов символов, и разработчик в данном случае не имеет возможности сделать выбор. Новый метод позволяит использовать другой, более легкий путь, для выбора конкретного набора символов, позволяя вам переопределить выбранный контейнером Shift_JIS на EUC-JP, например.

Однако, история на этом не кончается. Ко всему прочему Servlet 2.4 вводит новый элемент <locale-encoding-mapping-list> в дескрипторе загрузки web.xml, чтобы дать "загрузчику" (deployer) возможность определить карту locale-to-charset вне кода сервлета. Это выглядит так:

  <locale-encoding-mapping-list>

    <locale-encoding-mapping>
      <locale>ja</locale>
      <encoding>Shift_JIS</encoding>
   </locale-encoding-mapping>

   <locale-encoding-mapping>
      <locale>zh_TW</locale>
      <encoding>Big5</encoding>
   </locale-encoding-mapping>

  </locale-encoding-mapping-list>

Теперь в Web приложении, любой ответ с установленной локалью ja использует набор символов Shift_JIS, и любые ответы с установленной китайско-тайваньской zh_TW локалью использует набор символов Big5. Позднее эти значения могут быть изменены, например, на UTF-8, когда он станет более популярным среди клиентов. Все остальные локали, не указанные в списке дескриптора, будут использовать значения, определенные по умолчанию контейнером, как это было раньше.

Изменения RequestDispatcher

В Servlet 2.4 добавлены пять новых атрибутов запроса для получения дополнительной информации при вызове RequestDispatcher forward(). Если вы помните, при вызове forward() в сервлете, контейнер изменяет окружение вызываемого сервлета так, как если бы это был первый вызванный сервлет. Методы getRequestURI(), getContextPath(), getServletPath(), getPathInfo(), и getQueryString() все возвращают информацию основанную на URI (Uniform Resource Identifier), переданную в getRequestDispatcher(). Однако, иногда в вызываемом с помощью forward() сервлете может быть необходим оригинальный URI. Для этого в Servlet 2.4 добавлены следующие атрибуты:

  • javax.servlet.forward.request_uri
  • javax.servlet.forward.context_path
  • javax.servlet.forward.servlet_path
  • javax.servlet.forward.path_info
  • javax.servlet.forward.query_string

В перенаправленном сервлете с помощью getRequestURI(), можно получить путь к сервлету, кроме того, если необходим исходный путь, можно вызвать request.getAttribute("javax.servlet.forward.request_uri")

Замечание
Если forward() происходит через вызов getNamedDispatcher(), эти атрибуты не устанавливаются потому что в этом случае начальный путь элементов не изменяется.

Этот набор атрибутов может вам напомнить атрибуты запроса, которые были добавлены в Servlet 2.2:

  • javax.servlet.include.request_uri
  • javax.servlet.include.context_path
  • javax.servlet.include.servlet_path
  • javax.servlet.include.path_info
  • javax.servlet.include.query_string

Однако, эти атрибуты работают совсем не так, как атрибуты forward(). В include(), элементы пути не изменяются, поэтому атрибуты include удобно использовать, когда нужен доступ к элементам целевого сервлета. В то время как в forward(), элементы пути изменяются, и он удобен, когда необоходим доступ к элементам исходного сервлета. Да, это немного запутанно. Как только сервлеты начали использовать пространство URI в качестве механизма внутренней диспетчеризации, открылся широкий путь для новых сложностей.

Другая область, где появляются новые вопросы — взаимодействие между RequestDispatcher и фильтрами. Как должны запускаться фильтры для перенаправленного сервлета ? Как насчет URI, вызываемых через механизм <error-page> ? До Servlet 2.4 эти вопросы оставались открытыми. Сейчас Servlet 2.4 дает право выбора пользователю. В дескрипторе загрузки появился новый элемент <dispatcher> с возможными значениями REQUEST, FORWARD, INCLUDE, и ERROR. Вы можете добавить любое число компонентов <dispatcher> в <filter-mapping>, например так:

<filter-mapping>

  <filter-name>Logging Filter</filter-name>
  <url-pattern>/products/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>FORWARD</dispatcher>
</filter-mapping>

Теперь фильтры должны применяться к запросам напрямую от клиента, так же как к перенаправляемым сервлетам. Добавление значений INCLUDE и ERROR также указывает на то, что фильтр должен дополнительно применяться на включенном запросе и запросах <error-page>. Используйте эти возможности по вашему усмотрению. Если вы не определили никаких элементов <dispatcher>, по умолчанию установлен REQUEST.

Последнее изменение RequestDispatcher предназначено для того, чтобы при вызовах request.getRequestDispatcher() можно было использовать относительный путь. Путь будет интерпретирован относительно текущего пути запроса. Это незначительное изменение, но оно удобно, когда запрос перенаправляется в родственный (sibling) сервлет.

Слушатели

Servlet 2.3 вводит понятие слушателей контекста и сессий. Это — классы, которые могут следить за тем, когда контекст или сессия были инициализированы, или отслеживать время, когда они должны быть уничтожены, и когда атрибуты были добавлены или удалены из контекста или сессии. Servlet 2.4 расширяет модель слушателей запроса, позволяя отслеживать, как запрос создается и уничтожается, и, как атрибуты добавляются и удаляются из сервлета В Servlet 2.4 добавлены следующие классы:

  • ServletRequestListener
  • ServletRequestEvent
  • ServletRequestAttributeListener
  • ServletRequestAttributeEvent

Эти классы были смоделированы по примеру подобных им ServletContextListener, ServletContextEvent, ServletContextAttributeListener, и ServletContextAttributeEvent, и назначаются с помощью тех же элементов <listener>. Разнообразие слушателей запроса было добавлено, главным образом, для того, чтобы помочь отладчикам зацепиться за процесс управления сервлета. Количество других практических приложений, использующих это, не должно быть велико, поэтому в рамках этой статьи я не вдаюсь в детали.

Servlet 2.4 также вносит некоторую ясность в вопрос о процессе обработки исключения, выбрасываемого слушателем. Так как слушатель всегда запускается вне стека service(), исключение не может перехватываться при обработке сервлета. Хотя этот вопрос остается нерешенным в Proposed Final Draft 2, его отличие от предыдущей версии в том, что исключения, порожденные слушателем, будут обрабатываться директивой <error-page> если она существует, а если нет, то на стороне клиента будет возникать обычная ошибка 500.

Изменение в сессиях

Может быть наиболее популярным станет добавленный в Servlet 2.4 новый метод HttpSession.logout(). Этот метод обеспечивает надежный механизм отключения пользователей, которые входили в систему, используя стандартный механизм <auth-method> (BASIC, DIGEST, FORM, CLIENT-CERT). К сожалению, этот метод может оказаться в числе тех, которые будут опущены в релизе версии 2.4. Помещение в сессию вызова logout() предполагает, что сессия управляет процессом входа, в то время как, во всех случаях, кроме FORM, это не так. При аутентификации BASIC, DIGEST, и CLIENT-CERT, клиент держит данные о параметрах входа и предоставляет их серверу в процессе запроса. Вы можете вызвать logout() и очистить сессию, но клиент все еще будет высылать действующие данные. На сервере нет верного способа заставить клиента не посылать их. Это — одна из причин, почему так много Web страниц используют основанный на формах режим входа — он позволяет закончить работу путем обнуления сессии или удаления cookie на стороне клиента.

Другое изменение в сессиях версии 2.4 позволяет нулевые или отрицательные значения в элементе <session-timeout>, для того чтобы сессия не имела таймаута. В общем случае, таких экстремальных мер необходимо избегать, но иногда, как показывают результаты, оказывается полезным вручную вызывать invalidate(). Используйте это с осторожностью.

Наконец, в Servlet 2.4 становится обязательным требование, чтобы распределенная система или создавала IllegalArgumentException, если объект помещенный в сессию не может быть сериализован, или отправляла его по сети. Предполагается, что уход от игнорирования ошибки должен способствовать лучшей переносимости.

Прочие преобразования

Из всех преобразований Servlet 2.4, мне наверное больше всего нравится , что welcome файлы могут быть сервлетами. Это значит, что index.tea с обработчиком *.tea может быть файлом по умолчанию также как index.html или index.jsp. Большинство серверов поддерживали это и ранее, некоторые не поддерживали, теперь, с появлением 2.4, это требование становится обязательным.

Также, закреплено, что все библиотечные файлы, используемые контейнером помимо входящих в структуру WEB-INF, такие как например JARы, которые Tomcat загружает из $CATALINA_HOME/shared/lib, должны быть загружены тем же самым загрузчиком классов внутри одной JVM. Эти расширяет возможности коммуникации Web приложений, поскольку таким образом мы избегаем проблем возникновения ClassCastException.

Не рекомендовано к использованию

Только один класс будет переведен в не рекомендованные к использованию в Servlet 2.4, и этот класс в высшей степени заслуживает этого. SingleThreadModel (STM), который был плохой идеей с самого начала, в 2.4.объявлен не рекомендованным к использованию. STM — мертв! Долгие лета многопотоковому программированию! Хотя STM выглядит хорошо с первого взгляда, метод поочередного вызова, предлагаемый STM в действительности не дает никаких выгод в плане безопасности потока, он дает лишь ложное чувство безопасности. Группа экспертов единогласно решила перевести его в разряд не рекомендуемых к использованию. Чтобы узнать почему STM считается вредным см. Ресурсы.

Схема

Последнее изменение, о котором я хотел рассказать, связано не с кодом, а скорее с форматом. Файл web.xml, ранее определявшийся с помощью DTD, теперь определяется с помощью предложенных консорциумом W3C XML схем. Версия 2.4 должна понимать форматы дескрипторов версий 2.2 и 2.3 , но все новые элементы определяются только схемой.

Схема более многословный язык, чем DTD, более строгий в одних случаях и менее в других. Добавлены некоторые ограничения, как, например, уникальность <role-name>. Некоторые требования были ослаблены. Например, последовательность элементов <web-app> более не фиксирована, и <distributable/> может появляться любое количество раз без ошибки. Также, тэг <description/> теперь поддерживает атрибут xml:lang для указания языка описания, если он не английский.

Простые контейнеры сервлетов не делают проверки соответствия схеме, контейнеры с поддержкой J2EE делают. Спецификация предупреждает разработчиков — "Дескриптор загрузки должен соответствовать схеме" — это хороший совет для тех, кто заботится о совместимости. В большинстве случаев переход от DTD на XML схемы не затронет обычного программиста сервлетов, однако, это делает понимание нового web.xml более сложным, если у вас нет соответствующего руководства.

Одна из черт схемы (или баг, в зависимости от того, как вы смотрите на вещи) то, что элементы в файле web.xml могут быть определены в схемах других спецификаций J2EE. Так, пока схема Servlet 2.4 web.xml упоминает <message-destination>, <message-destination-ref>, и <service-ref> и определяет, где они могут находится в web.xml, актуальные определения этих элементов и их дочерние элементы импортируются. Также, некоторые элементы, которые ранее были определены внутри спецификации сервлетов, были убраны, и, связанные по имени, они теперь получают определение из спецификации J2EE. Этот список включает <env-entry>, <ejb-ref>, <ejb-local-ref>, <resource-ref>, <resource-env-ref>, и все их возможные дочерние элементы. Вы также увидите, что определение элемента <jsp-config> было перенесено в спецификацию JSP (JavaServer Pages), хотя его имя еще фигурирует в схеме.

Как всеми этими импортированными элементами предполагается управлять, не было разъяснено. Действительно, кажется странным, что технология, более низкого уровня ссылается на технологию расположенную выше, как сервлеты ссылаются на <service-ref> импортируемую из JAX-RPC. Это, как если бы TCP/IP нужно было знать об HTTP. Sun уже говорил, что standalone сервлеты не имеют высокого приоритета, что вероятно и объясняет этот интегрированный дизайн. Будет интересно посмотреть, что произойдет с этой спецификацией по мере ее развития.

Что вы не увидите

Версия 2.4 не реализует некоторые, запланированные ранее, интересные моменты. Первое — возможность расширения схемы, которая присутствовала до Public Final Draft и была убрана в Public Final Draft 2. Возможность расширения схем была предназначена для того, что бы третьи партии могли использовать свои элементы в файле web.xml. Это было убрано по указанию экспертной группы, потому что использование одного файла для конфигурации быстро создает неуправляемый беспорядок. Это все равно, как сохранять весь исходный код в одном файле.

Кое-что было отложено на будущее. Первое — новое API ввода-вывода, впечатляющие новые возможности J2SE, которые значительно ускоряют взаимодействие между клиентом и сервером, благодаря новой метафоре каналов, которая позволяет вам создавать буферы в системной памяти и файлах распределения памяти, управлять DMA, получать и устанавливать свойства устройств ввода-вывода. К сожалению, чтобы использовать в Servlet 2.4 новые каналы ввода/вывода, вам необходим J2SE 1.4, возможности которого использовать пока преждевременно. Разработчики серверов, конечно, могут использовать новые возможности ввода-вывода в своих реализациях, но сервлеты не смогут получить полное преимущество от нового API ввода-вывода, пока они не смогут получить правильный канал для связи с клиентом.

Также не включены никакие правила о том, как должны взаимодействовать HTTP и HTTPS интерфейсы на одном и том же сервере. Должны сессии быть одинаковыми или должны отличаться? Должны работать forward() и include(), или надо использовать sendRedirect()? Возможно эти вопросы будут освещены в следующем релизе.

Начало поддержки Servlet 2.4

Итак, Servlet 2.4 устанавливает новый минимальный набор требований, новые методы отслеживания запросов, новые методы управления ответом, новую поддержку интернационализации, несколько расширений RequestDispatcher, новые классы слушателей запросов, преобразования сессий, и, новые, основанные на XML схемах, дескрипторы загрузки, также как некоторых новых элементов J2EE. Вся спецификация была ужата, с целью убрать двусмысленность, которая может стать препятствием для кросс-платформенной переносимости. В конечном счете, спецификация включает четыре новых класса и семь методов добавленных в уже существовавшие классы, одна новая константа и один класс не рекомендованный к использованию. См. Шпаргалку для перехода от 2.3 на 2.4 ниже.

Об авторе

Джейсон Хантер автор книги Java Servlet Programming, 2nd Edition (O'Reilly, 2001; ISBN: 0596000405) и соавтор новой книги Java Enterprise Best Practices (O'Reilly, 2002; ISBN: 0596003846). Он участник проекта Apache, и, как представитель Apache в комитете Java Community Process, принимал участие в памятном соглашении об открытом исходном коде Java. Он публикуется на Servlets.com, один из основных участников Apache Tomcat, и член экспертной группы, ответственной за разработку Servlet/JSP и JAXP, является одним из создателей открытого исходного кода библиотеки JDOM, предназначенной для оптимизации взаимодействия Java и XML. Недавно он спроектировал и разработал CountryHawk, программный продукт, позволяющий быстро определить страну пользователя на основании его IP адреса.

Ресурсы

Переход от Servlet 2.3 к Servlet 2.4

Классы не рекомендованные к использованию SingleThreadModel
Не рекомендованные к использованию методы Нет
Новые классы ServletRequestAttributeListener, ServletRequestAttributeEvent, ServletRequestListener, ServletRequestEvent
Новые методы ServletRequest.getRemotePort(), ServletRequest.getLocalName(), ServletRequest.getLocalAddr(), ServletRequest.getLocalPort(), HttpSession.logout() (вероятно будет убран), ServletResponse.getContentType() ServletResponse.setCharacterEncoding()
Новые константы SC_FOUND для 302, так как в HTTP/1.1 изменилось имя
Новые тэги <dispatcher>, <locale-encoding-mapping-list>, <locale-encoding-mapping>, <locale>, <encoding>
Вынесенные тэги <env-entry>, <ejb-ref>, <ejb-local-ref>, <recode-ref>, <recode-env-ref>, <jsp-config>, и их дочерние элементы
Новые тэги J2EE <message-destination>, <message-destination-ref>, <service-ref>, и их дочерние тэги

Reprinted with permission from the March 2003 edition of JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://www.javaworld.com/javaworld/ jw-03-2003/jw-0328-servlet.html

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог