<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>LAR</title>
	<atom:link href="http://lar.ru/blog/feed" rel="self" type="application/rss+xml" />
	<link>http://lar.ru/blog</link>
	<description>Блог компании «ЛАР»</description>
	<pubDate>Wed, 17 Mar 2010 07:02:54 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.3</generator>
	<language>en</language>
			<item>
		<title>О «Ларе» в Бразилии</title>
		<link>http://lar.ru/blog/2010/03/17/285</link>
		<comments>http://lar.ru/blog/2010/03/17/285#comments</comments>
		<pubDate>Wed, 17 Mar 2010 07:02:54 +0000</pubDate>
		<dc:creator>Erlang</dc:creator>
		
		<category><![CDATA[Делимся опытом]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=285</guid>
		<description><![CDATA[]]></description>
			<content:encoded><![CDATA[<p>На сайте бразильской девушки-фотографа <a href="http://iemai.com.br/" target="_blank">Emil&atilde;ine Vieira</a> нашёл замечательное упоминание о компании «Лар»:</p>
<p><img src="http://iemai.com.br/blog/wp-content/uploads/2010/02/10fev09.jpg" alt="LAR" /><br />
______<br />
* Надпись на португальском языке переводится так: «Нет больше такого места, как наш домашний очаг». Lar — «домашний очаг» по-португальски.</p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2010/03/17/285/feed</wfw:commentRss>
		</item>
		<item>
		<title>Обзор CMS: Wordpress</title>
		<link>http://lar.ru/blog/2009/05/29/274</link>
		<comments>http://lar.ru/blog/2009/05/29/274#comments</comments>
		<pubDate>Fri, 29 May 2009 09:18:44 +0000</pubDate>
		<dc:creator>Erlang</dc:creator>
		
		<category><![CDATA[Делимся опытом]]></category>

		<category><![CDATA[Обзор CMS]]></category>

		<category><![CDATA[cms]]></category>

		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=274</guid>
		<description><![CDATA[Автор: Кирилл Панфилов
WordPress вообще очень хорошая блогообразная CMS; плюсом является то, что систему можно использовать не только как блог, но и как сайты другого типа. Например, форум прикрутить или назначить главной страницей статическую. В общем, при желании можно сделать из него конфетку.
Несомненно, 2-3 мегабайта исходников (это только в архиве) — явный перебор. Скорость работы из-за [...]]]></description>
			<content:encoded><![CDATA[<p><em>Автор: Кирилл Панфилов</em></p>
<p>WordPress вообще очень хорошая блогообразная CMS; плюсом является то, что систему можно использовать не только как блог, но и как сайты другого типа. Например, форум прикрутить или назначить главной страницей статическую. В общем, при желании можно сделать из него <a href="http://www.ilovecolors.com.ar/" target="_blank">конфетку</a>.<br />
Несомненно, 2-3 мегабайта исходников (это только в архиве) — явный перебор. Скорость работы из-за этого сильно снижается.<br />
Есть несколько вещей, которые кем-то могут считаться плюсами, а кем-то минусами.<br />
<span id="more-274"></span><br />
1. Система шаблонов. Почему в ней совмещены и HTML, и PHP-код — неясно. Можно было бы оставить на откуп шаблонам только разметку с системными тэгами, которые динамически заменялись бы на программную логику. Но в этом случае всю систему придётся переписать. С другой стороны, такой подход позволяет не только перекроить шаблон, но и добавлять собственный функционал.<br />
2. Работа с комментариями сделана, мягко говоря, непродуманно. Капчи (защиты) изначально нет: здравствуй, спам. Настроить вывод комментов для удаления нельзя. Отключить комментирование разом нельзя. На один из наших сайтов была спам-атака, за день 18 тысяч новых комментов, которые по 20 штук удалять было никак не оптимистично. Доступа к базе напрямую тоже нет, чтобы <a href="http://7bloggers.ru/7-wordpress-zadach-i-ix-reshenie/" target="_blank">удалить все неодобренные комменты</a> одним запросом: <code>DELETE from wp_comments WHERE comment_approved = '0';</code>. Страница модерирования от такого количества тоже умерла. Переименовал скрипт добавления комментов (а вот у меня во фреймворке одну галочку для этого надо поставить в настройках, чтобы запретить комментирование), изменил скрипт администрирования комментов на PHP, чтобы по 400 комментов разом можно было сносить (если больше, то система вешается и не отдаёт страницу в админке), поудалял комменты, нашёл плагин с капчей, активировал, обратно переименовал скрипт добавления&#8230;<br />
3. Плагины. Страшное дело. Это путь фаерфокса: без плагинов только основаная функциональность, очень-очень основная, зато с плагинами рай земной. Только чем больше плагинов, тем больше система тормозит. А функциональность-то нужна: нормальные адреса страниц, капча, форум, перевод фрагментов текста на русский язык (с локализацией беда вообще), управление комментариями, красоты всякие аджаксовые&#8230; А элементарный файловый менеджер? Попробуй его найди. Опять — ставить плагин&#8230; И просто попытка загрузить на свой же сайтоблог файлик, чтобы показать другу, превращается в мучение.<br />
4. Слишком большое количество файлов. Как следствие — б&oacute;льшая уязвимость системы (то, что это вордпресс, фактически невозможно скрыть: слишком много следов). Логика, конечно, предельно понятная, сразу знаешь, что где искать, но нужно ли, если как фреймворком пользуешься вордпрессом as is?</p>
<p>Итог. В качестве системы, которую быстро поставил, настроил и работай — очень удобно. Если нужна серьёзная основа для работы с большим наплывом пользователей — нужно ещё тринадцать раз подумать.</p>
<p><em>Исходная публикация: <a href="http://blog.erlang.com.ru/post.php?2009-5-16" target="_blank">blog.erlang.com.ru/post.php?2009-5-16</a></em></p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/05/29/274/feed</wfw:commentRss>
		</item>
		<item>
		<title>Система распределенных вычислений на базе сервисов Amazon</title>
		<link>http://lar.ru/blog/2009/04/22/263</link>
		<comments>http://lar.ru/blog/2009/04/22/263#comments</comments>
		<pubDate>Wed, 22 Apr 2009 10:45:14 +0000</pubDate>
		<dc:creator>Karina Meloyan</dc:creator>
		
		<category><![CDATA[Статьи]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=263</guid>
		<description><![CDATA[Целевая аудитория: технические руководители, системные архитекторы и веб-разработчики.
Проектирование и реализация веб-приложений, требующих интенсивной работы процессора, — задача не из числа тривиальных. В теории все знают, что если сервер не справляется с требуемым объемом вычислений за определенное время,  то нужно разбить задачу на мелкие и обрабатывать ее части параллельно на нескольких машинах. Этот подход известен [...]]]></description>
			<content:encoded><![CDATA[<p><em>Целевая аудитория: технические руководители, системные архитекторы и веб-разработчики.</em></p>
<p>Проектирование и реализация веб-приложений, требующих интенсивной работы процессора, — задача не из числа тривиальных. В теории все знают, что если сервер не справляется с требуемым объемом вычислений за определенное время,  то нужно разбить задачу на мелкие и обрабатывать ее части параллельно на нескольких машинах. <span id="more-263"></span>Этот подход известен как распределенные вычисления. Распространено мнение, что как разработка, так и поддержка серверного решения распределенных систем — трудоемкий и дорогой процесс, что является серьезным барьером для стартапов. В этой статье мы развеем этот миф, предложив практическое решение для такой задачи:</p>
<p>— данные для обработки поступают от заказчиков в виде задач через web в течение рабочего дня, однако обрабатывать их нужно в ночное время (например, с 00:00 до 06:00);<br />
— объем входных данных для обработки задачи, как и данных, представляющих результат, невелик (например, несколько килобайт);<br />
— все задачи и результаты должны быть зарегистрированы в единой базе;<br />
— обработка задачи требует значительного времени в масштабах среднестатистического веб-сервера (например несколько секунд);<br />
— количество задач, которые нужно обработать за ночь, довольно большое (тысячи).</p>
<p><strong>Hardware: Серверное решение<br />
</strong><br />
Серверное решение базируется на том, что главный сервер круглосуточно собирает данные от пользователей и передает их на обработку другим серверам, специально подобранным и сконфигурированным для выполнения ресурсоемких вычислений (таких серверов может быть десятки или сотни). Вычисления производятся на этих серверах ежедневно в ночное время, как требует поставленная задача.</p>
<p>Организация подобной системы довольно затратна, поскольку требуется приобрести, настроить и поддерживать достаточное количество серверов. Кроме того, налицо неэффективное использование ресурсов — вычислительные серверы будут простаивать в дневное время. Если требуется обеспечить запас мощности (например, возможно некоторое колебание дневного объема задач), то нужно иметь определенное количество серверов в резерве.</p>
<p>Учитывая требования к системе, мы предлагаем использовать сервис <a href="http://aws.amazon.com/ec2/" target="_blank">Amazon EC2</a> как хостинг-платформу для вычислительных серверов. Преимуществом этой платформы являетется то, что возможен динамический запуск виртуальных машин, причем оплате подлежат фактические часы использования. Если программная логика позволяет динамически оценить количество требуемых машин для обработки данных за ночь, то можно без участия человека запускать требуемое количество машин, разворачивая их из подготовленного образа. Когда вычисления закончены, разумно остановить все машины, чтобы не оплачивать время простоя.</p>
<p>Для работы с виртуальными серверами доступен API и множество реализующих его библиотек. В своей работе мы используем <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351&amp;categoryID=88http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351&amp;categoryID=88" target="_blank">Amazon EC2 API Tools</a>. Однако детальное рассмотрение возможностей API выходит за рамки данной статьи. Для главного сервера подходит мощный сервер как на традиционном хостинге, так и на Amazon EC2.</p>
<p><strong>Software: Архитектура</strong></p>
<p>Назовем модуль, работающий на главном сервере, DCBoss, а внешний модуль, установленный на вычислительных серверах, — DCWorker. Функция DCWorker проста — он получает задачу (или пакет задач) на обработку, производит вычисления и возвращает результат DCBoss.</p>
<p>Рассмотрим задачи DCBoss более детально. Главный модуль ответственен за следующие процедуры:</p>
<p>1. получение данных от заказчиков;<br />
2. распределение задач между копиями DCWorker, контроль выполнения;<br />
3. получение результатов от DCWorker и сохранение в центральной базе.</p>
<div id="attachment_264" class="wp-caption alignnone" style="width: 160px"><a href="http://lar.ru/blog/wp-content/uploads/2009/04/1.jpg" target="_blank"><img class="size-thumbnail wp-image-264" title="1" src="http://lar.ru/blog/wp-content/uploads/2009/04/1-150x150.jpg" alt="Рисунок 1" width="150" height="150" /></a><p class="wp-caption-text">Рисунок 1</p></div>
<p>Вторая задача довольно нетривиальна, но вполне стандартна. Чтобы не изобретать велосипед, мы использовали готовое решение — Amazon SQS, которое берет на себя разрешение вопросов диспетчеризации задач (блокировка задачи во время ее выполнения одной из копий DCWorker, повторная отправка задачи на выполнение в случае неуспешного завершения первой попытки и т.п.). Кроме того, в силу своей распределенной природы, Amazon SQS успешно справляется с тысячами одновременных запросов на получение новой задачи (в случае большого числа копий DCWorker).</p>
<p>Таким образом, мы пришли к модифицированной схеме процесса, где DCBoss выполняет следующие функции:</p>
<p>1. получение данных от заказчиков;<br />
2. отправка задач в очередь на базе Amazon SQS (назовем ее TaskQueue);<br />
3. получение результатов от DCWorker и сохранение в центральной базе.</p>
<div id="attachment_265" class="wp-caption alignnone" style="width: 160px"><a href="http://lar.ru/blog/wp-content/uploads/2009/04/2.jpg" target="_blank"><img class="size-thumbnail wp-image-265" title="2" src="http://lar.ru/blog/wp-content/uploads/2009/04/2-150x150.jpg" alt="Рисунок 2" width="150" height="150" /></a><p class="wp-caption-text">Рисунок 2</p></div>
<p>В получившейся схеме все же присутствует одно слабое место — получение результатов от DCWorker, которое может оказаться массовым. Чтобы избежать ситуации пиковых нагрузок на главный сервер, мы ввели в схему еще одно звено — вторую очередь на базе Amazon SQS, в которую DCWorker сохраняет результаты вычислений. Назовем эту очередь ResultQueue. DCBoss сам инициирует чтение результатов вычислений из ResultQueue, что позволяет избежать ситуации массовых обращений к главному серверу копиями DCWorker.</p>
<p>В данной схеме DCBoss ответственен за такие функции:</p>
<p>1. получение данных от заказчиков;<br />
2. отправка задач в TaskQueue;<br />
3. получение результатов из ResultQueue  и сохранение их в центральной базе.</p>
<div id="attachment_266" class="wp-caption alignnone" style="width: 160px"><a href="http://lar.ru/blog/wp-content/uploads/2009/04/3.jpg" target="_blank"><img class="size-thumbnail wp-image-266" title="3" src="http://lar.ru/blog/wp-content/uploads/2009/04/3-150x150.jpg" alt="Рисунок 3" width="150" height="150" /></a><p class="wp-caption-text">Рисунок 3</p></div>
<p>При введении в систему элементов на базе Amazon SQS следует учитывать некоторые особенности этой реализации очереди.</p>
<p>— Принцип FIFO не соблюдается, и элементы возвращаются очередью в случайном порядке. Для нашей задачи это не принципиально, так как в момент запуска вычислений прием новых задач от заказчиков приостанавливается, и рассчитанные вычислительные мощности должны справиться с поступившими задачами.<br />
— Amazon SQS гарантирует, что элемент очереди будет возвращен <strong>не менее</strong> одного раза, что означает вероятность обработки одной задачи несколькими копиями DCWorker.<br />
В результате мы добились довольно гибкой схемы, где есть возможность управлять вычислительными мощностями за счет изменения количества копий DCWorker.</p>
<p><strong>Software: Реализация<br />
</strong><br />
DCBoss и DCWorker — это два независимых модуля, которые могут быть реализованы на разных языках программирования  и/или запущены на разных платформах, поскольку они взаимодействуют между собой только через Amazon SQS API. Мы выбрали для реализации соответственно Java и PHP, поскольку для них доступны библиотеки, реализующие SQS API. Хотя, конечно, можно реализовать все необходимые функции самостоятельно и на другом языке.</p>
<p>TaskQueue и ResultQueue оперируют с текстовыми сообщениями, представляющими задания и результаты. У Amazon SQS есть ограничение: длина сообщения не должна превышать 8kb. Если такое ограничение неприемлемо для системы, то обойти его можно, как рекомендует Amazon: <em>«Сообщения большего размера можно хранить на Amazon Simple Storage Service (Amazon S3) или Amazon SimpleDB и использовать Amazon SQS для хранения указателя на объект Amazon S3 или Amazon SDB»</em>. [http://docs.amazonwebservices.com/AWSSimpleQueueService/2008-01-01/SQSGettingStartedGuide/]. С другой стороны, если, к примеру, объем сообщения не превышает 1kb, то можно оптимизировать передачу данных за счет объединения 8 заданий (или результатов)  в пакет.</p>
<p><strong>Демонстрационная система</strong></p>
<p>Мы предлагаем исходный код простой реализации модулей DCBoss и DCWorker. Данная реализация нацелена на подтверждение концепции и визуализацию идеи. Применение ее на практике потребует по меньшей мере реализации обработки исключительных ситуаций.</p>
<p>Модуль DCBoss реализован как веб-приложение на Java, основанное на веб-оболочке <a href="http://struts.apache.org/1.x/" target="_blank">Struts</a> и библиотеке <a href="http://code.google.com/p/typica/" target="_blank">Typica</a> для работы с Amazon SQS API. DCBoss позволяет пользователю создавать тестовую «задачу» и «запускать ее на выполнение» с возможностью наблюдать все этапы обработки. В демонстрационной системе пользователь вручную инициирует запуск копий DCWorker. Пользователь может получить информацию о том, какая из копий DCWorker обработала определенную задачу.<br />
DCWorker — это скрипт на PHP с заглушкой на месте предполагаемой обрабатывающей функции. Здесь используется библиотека <a href="http://code.google.com/p/simple-aws/" target="_blank">simple-aws</a> для работы с Amazon SQS. DCWorker просто читает задачи из TaskQueue и помещает сообщения с добавлением своего идентификатора в ResultQueue. Для простоты копии DCWorker — всего лишь экземпляры PHP-класса, эмулирующие работу виртуальной машины и выполняющие обработку на одном сервере как обработчики HTTP-запросов.</p>
<p>В приложенном файле readme.txt можно найти информацию о том, как настроить оба модуля.</p>
<p>Современные технологии позволяют сделать даже масштабные по объемам вычислений решения вполне доступными. Amazon предоставляет целый ряд мощных средств для их реализации — Amazon SQS и Amazon EC2, рассмотренные в этой статье — только примеры, которые мы успешно применяем в нашей работе.</p>
<p><a href="/blog/wp-content/uploads/lar-dc.zip" target="_self">Исходный код </a></p>
<p><em><strong>Автор: Екатерина Журданова</strong></em></p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/04/22/263/feed</wfw:commentRss>
		</item>
		<item>
		<title>Занятие 4. Перехватчики</title>
		<link>http://lar.ru/blog/2009/04/01/194</link>
		<comments>http://lar.ru/blog/2009/04/01/194#comments</comments>
		<pubDate>Wed, 01 Apr 2009 12:00:47 +0000</pubDate>
		<dc:creator>aracus</dc:creator>
		
		<category><![CDATA[Учебный курс]]></category>

		<category><![CDATA[Java]]></category>

		<category><![CDATA[Struts]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=194</guid>
		<description><![CDATA[Interceptor
В этой части мы завершаем рассмотрение примера с авторизацией пользователя. Нам нужно учесть, что когда пользователь авторизовался, то система должна запомнить этого пользователя. Дальнейшие действия (экшны) будут обрабатываться с учетом того, что пользователь уже авторизован.
Если же данные пользователя не найдены, только тогда предложить ему ввести их (перенаправить действие на login.action).
Замечание: Для простоты мы будем хранить [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Interceptor</strong><br />
В этой части мы завершаем рассмотрение примера с авторизацией пользователя. Нам нужно учесть, что когда пользователь авторизовался, то система должна запомнить этого пользователя. Дальнейшие действия (экшны) будут обрабатываться с учетом того, что пользователь <span style="text-decoration: underline;">уже авторизован</span>.<br />
Если же данные пользователя не найдены, только тогда предложить ему ввести их (перенаправить действие на login.action).</p>
<p><strong>Замечание:</strong> Для простоты мы будем хранить пользователя (объект) в http-сессии.</p>
<p>Одним из решений такой проблемы является проверка внутри каждого экшна (хранится ли в сессии такой-то пользователь), но это неэкономно. Гораздо удобнее осуществлять подобную проверку до вызова экшна. Подобный подход в Struts 2 реализуется с помощью механизма <em>перехватчиков, или интерцепторов (interceptors)</em>.<br />
<span id="more-194"></span></p>
<p><a href="http://lar.ru/blog/wp-content/uploads/2009/04/schema4_v2.png"><img class="aligncenter size-full wp-image-195" title="schema4_v2" src="http://lar.ru/blog/wp-content/uploads/2009/04/schema4_v2.png" alt="" width="300" height="251" /></a>Как видно из простой схемы (см. иллюстрацию выше), перехватчики могут выполняться до и/или после выполнения экшнов. В результате своей работы перехватчик может вызвать действие, или предотвратить его выполнение, или перенаправить дальнейшую обработку третьему действию. Все перехватчики, так же как и экшны, складываются в стек и выполняются в указанном порядке, поэтому подряд могут выполняться сразу несколько интерцепторов.</p>
<p><strong>Хранение пользователя в сессии</strong><br />
Это отступление необходимо, т.к. позволит сократить дальнейшие объяснения по реализации собственного интрецептора, но останавливаться на них подробно не будем. Все изменения несложные и очевидные.<br />
Сначала создаем класс User:</p>
<pre><code>public class User implements Serializable {

	private String nick;
	private String password;

    public User(String nick, String password){
        this.nick = nick;
        this.password = password;
    }
	public String getNick() {
		return nick;
	}
	public void setNick(String nick) {
		this.nick = nick;
	}

	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}</code></pre>
<p>Создаем класс &lt;code&gt;AuthController&lt;/code&gt;, отвечающий за помещение объекта User в сессию с некоторым ключом и извлечение из сессии по тому же ключу:</p>
<pre><code>public class AuthController {

	private static final String SESSION_KEY = "authUser";

	public static User getUser() {
		return (User)ActionContext.getContext().
getSession().get(SESSION_KEY);
	}

	public static void setUser(User user) {
		ActionContext.getContext().getSession().
put(SESSION_KEY, user);
	}
}</code></pre>
<p>Исправляем <code>LoginAction</code>:</p>
<pre><code>//добавляем поле user типа User
private User user;
....
//исправляем валидацию с учетом появления нового объекта
public void validate() {
		if (!this.getFieldErrors().isEmpty()) return;
        this.user = new User(this.getNick(),this.getPassword());
		if (!"user".equalsIgnoreCase(this.user.getNick()) ||
 !"user".equalsIgnoreCase(this.user.getPassword()))
	    	this.addFieldError("invalidLoginPassw",
 "Invalid login/password");       

	}
...
//кладем user в сессию, если валидация пройдена
public String loginSubmit() throws Exception {
		AuthController.setUser(this.user);
		return SUCCESS;
	}</code></pre>
<p><strong>ActionInvocation</strong><br />
Когда фреймворк получает запрос от клиента, он в первую очередь решает, какой экшн нужно выполнить. Но вместо того,  чтобы выполнять действие напрямую, создается объект <code>ActionInvocation</code>, который инкапсулирует экшн и интерцепторы, выполняемые до и/или после выполнения действия. Вообще <code>ActionInvocation</code> инкапсулирует все детали обработки текущего действия.</p>
<p>Итак, в начале обработки создается экземпляр класса соответствующего экшна. Этот экземпляр помещается в новый экземпляр ActionInvocation. Информация об экшне, как мы помним, берется из <code>struts.xml</code>. Из того же конфигурационного файла получаем информацию об интерцепторах, которые связаны с этим действием, и об их порядке (что именно добавляется в <code>struts.xml</code>, покажем ниже). Ссылки на перехватчики в нужном порядке так же добавляются к <code>ActionInvocation</code>. <code>ActionInvocation</code> отвечает за последовательный вызов и выполнение перехватчиков из стека, вызывая у каждого из них метод <code>intercept()</code>. Заметим, что этот метод содержит <code>ActionInvocation</code> в качестве параметра. После выполнения всех перехватчиков из стека вызывается выполнение экшна (фреймворк вызывает метод <code>invoke()</code>). В общем случае <code>ActionInvocation</code> проходит весь стек перехватчиков и передает дальнейшую обработку экшну, пришедшему от клиента. Выполнение действия происходит так же, как мы рассматривали в первом уроке.</p>
<p><strong>Создание собственного перехватчика</strong><br />
Вначале создаем абстрактный класс <code>ActionBase</code>, который содержит всего одно поле <code>checkAuth</code>:</p>
<pre><code>public abstract class ActionBase extends ActionSupport {

	private boolean checkAuth;

	public boolean isCheckAuth() {
		return checkAuth;
	}
	public void setCheckAuth(boolean checkAuth) {
		this.checkAuth = checkAuth;
	}

}</code></pre>
<p>Это поле <code>checkAuth</code> отвечает за то, нужно ли для данного экшна проверять наличие пользователя в сессии.</p>
<p>Все классы, обрабатывающие экшны в примере, должны наследовать этот абстрактный класс.</p>
<p>Теперь переходим к самому перехватчику.<br />
Наш класс должен наследовать интерфейс Interceptor, от которого нужно реализовать метод <code>intercept()</code>:</p>
<pre><code>public class AuthenticationInterceptor implements Interceptor {

	public String intercept(ActionInvocation actionInvocation)
 throws Exception {
		Object action = actionInvocation.getAction();
		if (action != null &amp;&amp; (action instanceof ActionBase) &amp;&amp;
(ActionBase)action).isCheckAuth()) {
			if (AuthController.getUser() == null)
				return "redirectToLogin";
		}
		return actionInvocation.invoke();
	}
	...
}</code></pre>
<p>Метод <code>intercept()</code> принимает <code>ActionInvocation</code> в качестве параметра, из которого получаем <code>Action</code>.  Перехватчик проверяет, свойство экшна <code>checkAuth</code> и наличие пользователя в сессии. Если для полученного экшна не нужно выполнять проверку или пользователь найден в сессии, то интерцептор передает управление первоначальному экшну. Иначе интерцептор возвращает строку «redirectToLogin».</p>
<p>На <a href="http://lar.ru/blog/2008/12/02/53">первом занятии</a> мы выяснили, что для каждого экшна в <code>struts.xml</code> определен результат, например:</p>
<pre><code> &lt;result type="dispatcher" name="success"&gt;/WEB-INF/jsp/login.jsp&lt;/result&gt; </code></pre>
<p>Какой именно результат (а их может быть несколько) будет выполняться, указывается параметром name. Интерцептор в любом случае тоже должен вернуть какой-либо результат. В случае, если пользователь не найден в сессии, возвращается строка. По этому значению фреймворк будет искать соответствующий результат в <code>struts.xml</code>. Однако результат, который мы пытаемся получить, не относится к какому-либо экшну, а определен независимо от них и называется <code>global result</code>. Определяется <code>global result</code> так:</p>
<pre style="text-align: left;"><code>&lt;global-results&gt;
    &lt;result type="redirect" name="redirectToLogin"&gt;login.action&lt;/result&gt;
&lt;/global-results&gt;</code></pre>
<p>Как видим, в результате произойдет редирект на <code>login.action</code>.</p>
<p>Поскольку обработка экшнов начинается с обращения к <code>struts.xml</code>, то наш интерцептор также должен быть указан в этом файле:</p>
<pre style="text-align: left;"><code>&lt;interceptors&lt;
   interceptor name="checkAuthInterceptor"
     class="mymovieteach.interceptor.AuthenticationInterceptor"/&gt;
    &lt;interceptor-stack name="defStack" &gt;
       &lt;interceptor-ref name="defaultStack" /&gt;
       &lt;interceptor-ref name="checkAuthInterceptor" /&gt;
    &lt;/interceptor-stack&gt;
&lt;/interceptors&gt;
</code></pre>
<p>Все, что касается перехватчиков, определяется внутри тега <code>&lt;interceptors&gt;</code>. Тегом <code>&lt;interceptor&gt; </code>мы определяем свой собственный интерцептор. Атрибут <code>name</code>, очевидно, отвечает за имя интерцептора, а атрибут <code>class</code> — за полное имя класса, его обрабатывающего.</p>
<p>С помощью тега <code>&lt;inteceptor-stack&gt;</code> мы задаем стек перехватчиков, перечисляя их по имени. Первым в стеке указан <code>defaultStack</code>, он предоставляется самим фреймворком и отвечает, например, за доставку параметров в экшн и загрузку файлов. Следом указан определенный выше перехватчик <code>checkAuth</code>.</p>
<p>Остался последний шаг. Укажем в <code>struts.xml</code> значение параметра <code>checkAuth=true</code> для <code>home.action</code>:</p>
<pre style="text-align: left;"><code>&lt;action name="home" class="mymovieteach.action.HomeAction"
method="show"&gt;
    &lt;param name="checkAuth"&gt;true&lt;/param&gt;
    &lt;result type="tiles"&gt;home&lt;/result&gt;
&lt;/action&gt; </code></pre>
<p>Таким образом, интерцептор будет выполнять свою проверку только для этого экшна. Для <code>login.action</code> проверка не имеет смысла, поэтому интерцептор сразу передаст управление самому экшну.</p>
<p><strong>Приложение 1:</strong><br />
<em>полный листтинг конфигурационнго файла <code>struts.xml</code></em></p>
<pre><code>&lt;struts&gt;
&lt;package name="common" extends="tiles-default"&gt;
  &lt;interceptors&gt;
      &lt;interceptor name="checkAuthInterceptor"
        class="mymovieteach.interceptor.AuthenticationInterceptor"/&gt;
        &lt;interceptor-stack name="defStack"&gt;
           &lt;interceptor-ref name="defaultStack" /&gt;
           &lt;interceptor-ref name="checkAuthInterceptor" /&gt;
        &lt;/interceptor-stack&gt;
  &lt;/interceptors&gt;
  &lt;default-interceptor-ref name="defStack"/&gt;
  &lt;global-results&gt;
      &lt;result type="redirect" name="redirectToLogin"&gt;login.action&lt;/result&gt;
  &lt;/global-results&gt;
  &lt;action name="login" class="mymovieteach.action.LoginAction" method="input"&gt;
      &lt;result type="tiles"&gt;login&lt;/result&gt;
  &lt;/action&gt;
  &lt;action name="loginSubmit" class="mymovieteach.action.LoginAction"
      method="loginSubmit"&gt;
      &lt;result type="tiles" name="input"&gt;login&lt;/result&gt;
      &lt;result type="redirect"&gt;home.action&lt;/result&gt;
  &lt;/action&gt;
  &lt;action name="home" class="mymovieteach.action.HomeAction" method="show"&gt;
      &lt;param name="checkAuth"&gt;true&lt;/param&gt;
      &lt;result type="tiles"&gt;home&lt;/result&gt;
  &lt;/action&gt;
&lt;/package&gt;
&lt;/struts&gt;
</code></pre>
<p><strong>Приложение 2:</strong><br />
<em>полный листинг класса <code>AuthenticationInterceptor</code></em></p>
<pre><code>package mymovieteach.interceptor;

import mymovieteach.action.ActionBase;
import mymovieteach.auth.AuthController;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

public class AuthenticationInterceptor implements Interceptor {

	public String intercept(ActionInvocation actionInvocation) throws Exception {
		Object action = actionInvocation.getAction();
		if (action != null &amp;&amp; (action instanceof ActionBase) &amp;&amp;
((ActionBase)action).isCheckAuth()) {
			if (AuthController.getUser() == null)
				return "redirectToLogin";
		}
		return actionInvocation.invoke();
	}

	public void init() {
	}

	public void destroy() {
	}
}</code></pre>
<p><strong>Упражнение 1</strong><br />
Предлагаем вам на основе всех четырех уроков самостоятельно добавить в приложение регистрацию пользователей. Для определенности считаем, что на форме регистрации должны быть следующие поля: ник, пароль, подтверждение пароля, email и чекбокс «I agree with terms and conditions».</p>
<p>Исходный код примера вы найдете в архиве (см. ниже). Здесь реализованы не только авторизация пользователя, но и регистрация. Вы можете подсмотреть код при выполнении упражнения, если возникнут какие-либо проблемы или трудности.</p>
<p><a href="http://lar.ru/blog/wp-content/uploads/lessons/lesson4_src.zip">Исходный код к уроку (и упражнению)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/04/01/194/feed</wfw:commentRss>
		</item>
		<item>
		<title>Корпоративные правила работы с MySQL</title>
		<link>http://lar.ru/blog/2009/03/16/180</link>
		<comments>http://lar.ru/blog/2009/03/16/180#comments</comments>
		<pubDate>Mon, 16 Mar 2009 10:15:03 +0000</pubDate>
		<dc:creator>Erlang</dc:creator>
		
		<category><![CDATA[Делимся опытом]]></category>

		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=180</guid>
		<description><![CDATA[Как и обе предыдущие статьи, эта демонстрирует, как можно унифицировать правила корпоративной работы над проектом.

Имена таблиц
— В именах таблиц используются только строчные буквы.
men
— Имена в многословных названиях разделяются подчеркиванием.
user_details
Имена полей
— В именах полей используются только строчные буквы.
— Шаблон: {3-буквенный префикс по имени таблицы}_{название поля или название поля на которое ссылаемся}
Пример
Таблица name
Поля:
nam_id — int
nam_name — varchar(255)
Таблица [...]]]></description>
			<content:encoded><![CDATA[<p>Как и обе предыдущие статьи, эта демонстрирует, как можно унифицировать правила корпоративной работы над проектом.</p>
<p><span id="more-180"></span></p>
<h3>Имена таблиц</h3>
<p>— В именах таблиц используются только строчные буквы.</p>
<pre>men</pre>
<p>— Имена в многословных названиях разделяются подчеркиванием.</p>
<pre>user_details</pre>
<h3>Имена полей</h3>
<p>— В именах полей используются только строчные буквы.<br />
— Шаблон: {3-буквенный префикс по имени таблицы}_{название поля или название поля на которое ссылаемся}<br />
<strong>Пример</strong></p>
<p><em>Таблица name<br />
Поля:<br />
nam_id — int<br />
nam_name — varchar(255)</em></p>
<p><em>Таблица men<br />
Поля:<br />
men_id — int<br />
men_nam_id — int<br />
men_men_matherId — int<br />
men_men_fatherId — int</em></p>
<p><strong>Как читать</strong></p>
<p>1) name — говорит что это таблица. Например v_name —это уже VIEW</p>
<p>2) nam_id — «nam»– это 3-буквенный префикс, он один на все таблицу и должен быть уникальным, как и имя таблицы. nam_id – идентификатор записи в таблицы name.</p>
<p>3) men_nam_id — тут мы читаем как: поле из таблицы men (нам об этом говорит префикс «men») и что оно ссылается на поле nam_id из таблицы nam</p>
<p>4) Нам нужно сделать две ссылки на таблицу nam — men_men_matherId и men_men_fatherId, которые будут ссылатся на одно и тоже поле man_id, но не можем сделать два поля с одинаковым именем mem_men_id, поэтому мы перед id подписывает уточнения: mather и father.</p>
<h3>Типы таблиц и кодировки</h3>
<p>Все приложения разрабатываемый для клиентов рано или поздно требуют поддержку unicode поэтому таблицы должны изначально ее поддерживать. Для поддержки транзакций таблицы должны быть типа InnoDB</p>
<pre>CREATE TABLE `letter` (</pre>
<pre>  `ltr_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,</pre>
<pre>  `ltr_title` char(255) NOT NULL,</pre>
<pre>  `ltr_shortdescription` text NOT NULL,</pre>
<pre>  `ltr_cmp_id` int(11) DEFAULT NULL,</pre>
<pre>  PRIMARY KEY  (`ltr_id`),</pre>
<pre>  UNIQUE KEY `ltr_id` (`ltr_id`)</pre>
<pre>) ENGINE=InnoDB DEFAULT CHARSET=utf8</pre>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/03/16/180/feed</wfw:commentRss>
		</item>
		<item>
		<title>Правила написания кода на Java</title>
		<link>http://lar.ru/blog/2009/03/16/175</link>
		<comments>http://lar.ru/blog/2009/03/16/175#comments</comments>
		<pubDate>Mon, 16 Mar 2009 09:50:41 +0000</pubDate>
		<dc:creator>Erlang</dc:creator>
		
		<category><![CDATA[Делимся опытом]]></category>

		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=175</guid>
		<description><![CDATA[Такие правила приняты в нашей компании. Они позволяют делать код более читаемым и аккуратным, избегать ошибок, связанных с разными стилями написания кода у людей, работающих над проектом, и в будущем легко редактировать его.

Имена файлов, пакетов
— В именах пакетов используются только строчные буквы.
staffavailability
— Имена Java-классам даются согласно стандартной нотации Java.
StaffAvailability
— Имена классов должны быть существительными, первые [...]]]></description>
			<content:encoded><![CDATA[<p>Такие правила приняты в нашей компании. Они позволяют делать код более читаемым и аккуратным, избегать ошибок, связанных с разными стилями написания кода у людей, работающих над проектом, и в будущем легко редактировать его.</p>
<p><span id="more-175"></span></p>
<h3>Имена файлов, пакетов</h3>
<p>— В именах пакетов используются только строчные буквы.</p>
<pre>staffavailability</pre>
<p>— Имена Java-классам даются согласно стандартной нотации Java.</p>
<pre>StaffAvailability</pre>
<p>— Имена классов должны быть существительными, первые буквы всех слов — заглавные.<br />
— В именах web-папок и файлов используются только строчные буквы. Слова в многословных названиях разделяются подчеркиванием.</p>
<pre>staff_availability</pre>
<h3>Имена методов, переменных</h3>
<p>— Названия методов должны быть глаголами, первая буква должна быть строчной, первые буквы внутренних слов — заглавные.<br />
— Имена переменных должны начинаться со строчной буквы, внутренние слова — с заглавной.<br />
— Имена констант составляются из всех заглавных букв, разделенных на слова символом подчеркивания.</p>
<h3>Отступы, длина строки, переносы строк</h3>
<p>— Отступы должны составлять строго 4 пробела (не знак табуляции).<br />
— Длина строки не должна превышать 80 символов.<br />
— Если длина выражения превышает длину строки, то необходимо разбить его на несколько строк согласно следующим правилам:<br />
1. перенос после запятой;<br />
2. перенос перед оператором;<br />
3. необходимо использовать отступ 8 пробелов для обозначения второй строки разделенного выражения. Последующие строки выравниваются по второй строке либо добавляются новые 8 пробелов для обозначения вложенности.</p>
<pre>int result = function1(longExpression1,</pre>
<pre>         function2(longExpression2,</pre>
<pre>                longExpression3));</pre>
<h3>Расположение блоков, операторов, пробелы, скобки</h3>
<p>— Определение переменных нужно располагать в начале блока, а не «ждать» первого использования переменной. Инициализация должна производиться, по возможности, сразу.</p>
<pre>void myMethod() {</pre>
<pre>    int count  = 0; // beginning of method block</pre>
<pre>    if (condition) {</pre>
<pre>        int int2; // beginning of "if" block</pre>
<pre>        ...</pre>
<pre>    }</pre>
<pre>}</pre>
<p>- Между именем метода и скобками для списка параметров нет пробела.</p>
<pre>getStaffList(String name)</pre>
<p>— Параметры разделяются пробелом.</p>
<pre>getStaffList(String name, int count)</pre>
<p>— Пробелы окружают любой оператор.</p>
<pre>res = getCount();</pre>
<pre>(a &gt; 10) ? b : c;</pre>
<p>— Ключевое слово и следующая за ним скобка ( должны разделяться пробелом.</p>
<pre>while (b &lt; 100){</pre>
<pre>    …</pre>
<pre>}</pre>
<p>— Открывающаяся скобка { располагается на той же строке, что и сигнатура метода/заголовок if, while-блока и т.п.<br />
— Закрывающаяся скобка } выровнена по строке начала данного блока.</p>
<pre>public Collection getStaffList(String name) {</pre>
<pre>    	Collection contacts = Contact.getList(name, RoleHelper.ROLE_AGENT, null,</pre>
<pre>    			AccessLevel.getStaffDiaryEditLevel(), 0, MAX_NUMBER);</pre>
<pre>    	return ListHelper.getLabelValueList(contacts);</pre>
<pre>    }</pre>
<p>— Методы разделяются пустой строкой, объявления свойств класса располагаются по одному на строку.<br />
— На строке располагается только один оператор.</p>
<pre>if (b) {</pre>
<pre>    return result;</pre>
<pre>}</pre>
<h3>Структурирование кода</h3>
<p>— Методы должны быть короткими, и выполнять только одну задачу (к примеру, почти любой цикл уже достоин того, чтобы вынести его в особый метод).<br />
— Имена методов должны быть самодокументированными.<br />
— Шаблоны ООП должны применяться для структурирования и облегчения восприятия.</p>
<p>Стандарт разработан на основе “Code Conventions for the Java Programming Language” by Sun (<a href="http://java.sun.com/docs/codeconv/" target="_blank">http://java.sun.com/docs/codeconv/</a>) с учетом принятых в коллективе практик.</p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/03/16/175/feed</wfw:commentRss>
		</item>
		<item>
		<title>Принципы корректных HTML и CSS</title>
		<link>http://lar.ru/blog/2009/03/16/168</link>
		<comments>http://lar.ru/blog/2009/03/16/168#comments</comments>
		<pubDate>Mon, 16 Mar 2009 09:07:41 +0000</pubDate>
		<dc:creator>Erlang</dc:creator>
		
		<category><![CDATA[Делимся опытом]]></category>

		<category><![CDATA[CSS]]></category>

		<category><![CDATA[HTML]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=168</guid>
		<description><![CDATA[Данная статья начинает небольшой цикл, в котором мы поделимся принятыми у нас в организации правилами написания кода. Корпоративные правила — не простая формализация процесса, а средство оптимизировать работу: если все придерживаются одних и тех же правил, код, написанный одним человеком, будет легко восприниматься другим, а значит, и работа движется быстрее.

Семантическая вёрстка
Часто термин «семантическая вёрстка» используют [...]]]></description>
			<content:encoded><![CDATA[<p>Данная статья начинает небольшой цикл, в котором мы поделимся принятыми у нас в организации правилами написания кода. Корпоративные правила — не простая формализация процесса, а средство оптимизировать работу: если все придерживаются одних и тех же правил, код, написанный одним человеком, будет легко восприниматься другим, а значит, и работа движется быстрее.</p>
<p><span id="more-168"></span></p>
<h3>Семантическая вёрстка</h3>
<p>Часто термин «семантическая вёрстка» используют не совсем корректно, в плане просто отделения формы от содержимого. Допустим, есть страница, свёрстанная на блоках, а всё разукрашивание ведётся средствами CSS. Логика и оформление в этом случае разделены, но при одном условии: если идентификация блоков ведётся не по внешним признакам, а действительно по семантике. Распространённая ошибка многих веб-верстальщиков — использовать идентификаторы и классы по их верстальным возможностям. Например, специальный блок для завершения группы плавающих элементов. Span, выделяемый красным цветом. Одинаковый класс и для эпиграфа, и для авторской подписи к материалу (выравнивание по правому краю + курсив). Чем это плохо: если возникнет необходимость изменить стилевое описание элементов, используемых на одной странице, то есть опасность, что «поедет» вёрстка или проявятся нежелательные изменения на страницах другого типа, где применены те же классы. (При редизайне установили для блока ширину в 415px вместо 512, но не проверили, что на одной из страниц из-за этого съёжились поля для ввода и лейблы, заключённые в блок с тем же стилевым идентификатором).</p>
<h3>Принципы правильной вёрстки</h3>
<p>1. Использовать только смысловые имена. Если блок для формы регистрации и блок для отправки писем похожи по каким-то признакам или на данном этапе вообще обладают одинаковыми стилями, лучше сделать два идентичных описания, но для разных ID или классов. В будущем вы обезопасите себя от багов в вёрстке, случившихся по недосмотру.</p>
<p>2. Если стилей получается слишком много, можно использовать несколько стилевых файлов: один основной, для классов и идентификаторов, встречающихся на большинстве страниц, остальные по разделам. Соответственно и подгружать нужные в соответствующих разделах (это легко поручить серверу).</p>
<p>3. Целесообразно использовать префиксы в именах классов и идентификаторов, например: regform_main, regform_input, regform_label, regform_announcement и так далее (помимо всего прочего, это создаст удобства при динамическом изменении стилей средствами JavaScript или серверного языка).</p>
<p>В стилевых файлах очень удобно снабжать комментариями разделы, например:</p>
<pre>/* Main */</pre>
<pre>a { ... }</pre>
<pre>p { ... }</pre>
<pre>/* Forms */</pre>
<pre>input { ... }</pre>
<pre>input.abcd { ... }</pre>
<pre>/* IDs */</pre>
<pre>#abc { ... }</pre>
<pre>#pretend { ... }</pre>
<p>Старайтесь использовать в стилевых файлах только латиницу во избежание проблем некоторых браузеров в кодировкой: если страница на UTF, а стилевой файл на Windows-1251 с использованием кириллических символов, то Internet Explorer (до 6 версии точно) просто проигнорирует эту стилевую таблицу.</p>
<p>И, конечно, нужно использовать последовательный стиль вёрстки и написания стилевых файлов: отступы, особенности имён, стилевые правила в столбик или в строчку&#8230;</p>
<p>4. Можно в качестве вспомогательных мер использовать возможности наследования, или вложенности, как некий аналог ООП. Если я напишу:</p>
<pre>div#register input {...},</pre>
<p>то я буду точно уверен, что таким стилевым описанием я разукрашу только поля ввода, заключённые в блок с id=&#8221;register&#8221;, а не какие-либо другие.</p>
<p>А для всех полей ввода можно описывать общие правила, только если они точно и всегда будут соблюдаться на всех страницах сайта и соответствуют общему стилю.</p>
<p>5. И не нужно забывать про таблицы. Их некто не отменял, если они также используются семантически, то есть для табличных данных (статистика, результаты, цены и т. п.)</p>
<h3>Код на CSS</h3>
<p><strong>1. CSS-файл с помощью комментариев разбивается на разделы:</strong></p>
<ul>
<li>основное (*, p, a, формы и т.п.),</li>
<li>идентификаторы и классы, которые встречаются на всех страницах,</li>
<li>разделы по конкретным страницам или группам страниц</li>
</ul>
<p>В этом случае проще найти причинно-следственные связи глюков вёрстки</p>
<p><strong>2. Стандарт оформления одного стилевого описания:</strong></p>
<pre>селектор {</pre>
<pre> правило 1;</pre>
<pre> правило 2;</pre>
<pre> правило 3;</pre>
<pre> }</pre>
<p>Не забываем о точке с запятой, несколько раз исправлял; это в некоторых браузерах бывает критично.</p>
<p><strong>3. Желательное расположение правил в описании:</strong><br />
селектор {<br />
блочные правила (display, position, z-index, top, right, float, clear);<br />
правила, относящиеся к блокам (margin, padding, border, width, height);<br />
линейные правила (цвет и гарнитура текста, подчёркивание);<br />
разное (курсоры и т. п.)<br />
}<br />
<strong>4. Единицы измерения пишем вплотную к числам: в некоторых браузерах из-за пробела возникнут разночтения. </strong></p>
<p><strong>5. В начале CSS-файла обнуляем все отступы. </strong></p>
<pre>* {</pre>
<pre> margin:0;</pre>
<pre> padding:0;</pre>
<pre> border:0; /* Если не нужны рамки для элементов форм */</pre>
<pre> }</pre>
<p>Затем в каждом конкретном случае назначаем отступы и рамки индивидуально. В этом случае у нас ничего не едет в разных браузерах.<br />
<strong>6. Ограничения </strong></p>
<p>С помощью CSS невозможно описать некоторые вещи, чтобы они корректно отображались во всех браузерах. Для их эмуляции можно использовать графику и обходные пути, это чуть дольше (учитывать эти трудозатраты при обсуждении предложенного дизайна интерфейса), но реально.</p>
<p><em>Чекбоксы.</em> В Опере можно настроить цвета и размеры, в ИЕ частично (объёмная рамка остаётся всегда), ФФ игнорирует CSS для чекбоксов. Если нужно кардинально менять внешний вид, решение такое: блок с чекбоксом делать с position:relative, внутри блока до самого input ставить span с такими же размерами, как у блока с чекбоксом, но абсолютно позиционированный, а фон span’а в CSS написать и скриптом менять в зависимости от нажатия. При отсутствии картинок будут видны чекбоксы (если не прописать цвет фона, конечно), при наличии — заменители.</p>
<p><em>Радиобаттоны (переключатели).</em> То же самое, что и для чекбоксов. При CSS-изменении фона в Опере фон меняется внутри поля, в ИЕ вокруг него, в ФФ не меняется.</p>
<p><em>Поля для загрузки файлов.</em> Наименее поддающаяся правке часть формы. В ФФ даже нельзя регулировать длину поля, во всех браузерах внешний вид кнопки Browse/Обзор не редактируется, надпись зависит от языка браузера, на border все браузеры реагируют по-разному: кто рисует вокруг поля ввода, кто целиком обрамляет поле ввода + кнопку, кто игнорирует. Решение: поле сделать прозрачным:</p>
<pre> filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50); /* IE 5.5+*/</pre>
<pre> -moz-opacity: 0.5; /* Mozilla 1.6 и ниже */</pre>
<pre> -khtml-opacity: 0.5; /* Konqueror 3.1, Safari 1.1 */</pre>
<pre> opacity: 0.5; /* CSS3 - Mozilla 1.7b +, Firefox 0.9 +, Safari 1.2+, Opera 9 */</pre>
<p>Под поле положить поле ввода и нарисованную кнопку, точно позиционировав их, а поле для загрузки выровнять по правому краю. Получится, что при щелчке на видимой нарисованной кнопке на самом деле будет происходить щелчок по прозрачной кнопке, а информацию о выбранном файле нужно скриптом записывать в редактируемое видимое поле ввода.</p>
<p><em>Выпадающие списки. </em>Нормально редактируются в ФФ и Опере, в ИЕ остаётся объёмный край. Тэг optgroup, группирующий элементы option, во всех браузерах интерпретируется по-разному и не редактируется CSS.</p>
<p><em>Позиционирование. </em>При вставке флэш-объекта он всегда оказывается на переднем плане, даже если есть элемент с z-index:40000. Решение только в правильном построении блока embed/object:</p>
<pre>#flash {</pre>
<pre>  position: relative; /*or absolute*/</pre>
<pre>  z-index: 0;</pre>
<pre>}</pre>
<pre>&lt;div id="flash"&gt;</pre>
<pre>  &lt;object ...&gt;</pre>
<pre>    &lt;param name="wmode" value="opaque"&gt;</pre>
<pre>    &lt;embed ... wmode="transparent"&gt;</pre>
<pre>  &lt;/object&gt;</pre>
<pre>&lt;/div&gt;</pre>
<p><em>Фоновые изображения.</em> Желательно указывать не только картинку, но и цвет фона, чтобы без графики у блока не было прозрачного фона. В URL не надо использовать кавычки: IE 5 под Макинтошем, встретив их, вешается.</p>
<h3>Код на HTML</h3>
<p>Если ориентируемся на XHTML-вёрстку, в одиночных тэгах завершающий слэш ставим через пробел: <strong>&lt;br /&gt;</strong> — это нужно для корректного их чтения старыми браузерами. Нетскейп 4 и прочая архаика не знает тэга <strong>&lt;br/&gt;</strong> (без пробела), но корректно проигнорирует неизвестный атрибут / тэга <strong>&lt;br&gt;</strong>, а для новых браузеров всё равно, есть пробел или нет. Одиночные тэги: <strong>&lt;img /&gt;</strong>, <strong>&lt;br /&gt;</strong>, <strong>&lt;hr /&gt;</strong>, <strong>&lt;input /&gt;</strong>, <strong>&lt;meta /&gt;</strong>, <strong>&lt;link /&gt;</strong>, <strong>&lt;base /&gt;</strong> (последний не поддерживается спецификацией). В выпадающих списках всегда закрываем <strong>&lt;option&gt;вот так&lt;/option&gt;</strong>. Не закрывать тэги и писать их большими буквами — хамство (© Катя Журданова).</p>
<p>Для всех элементов форм используем и <strong>name</strong>, и <strong>id</strong> — если не нужно сразу, пригодится в дальнейшем.</p>
<p>Если чекбокс один в форме, то имя и идентификатор могут совпадать.</p>
<p>Для чекбоксов и радиобаттонов используем лейблы:</p>
<pre>&lt;input type="checkbox" name="checkbox1" id="checkbox1" value="1" /&gt;
<pre>&lt;label for="checkbox1"&gt;Нажимать тут&lt;/label&gt;</pre>
</pre>
<p>Желательно и для остальных полей ввода использовать не абстрактные блоки или <strong>span</strong>, а лейблы (<strong>&lt;label&gt;</strong>) — с помощью стилей их легко сделать и строчными, и блочными элементами, и в любом браузере они легко разукрашиваются.</p>
<p>Не гнушаемся абзацами. Часто так получается, что все мыслимые текстовые фрагменты заключены в «дивы», а для очистки от флоатинга потом используется <strong>&lt;br&gt;</strong> с каким-нибудь классом. Если использовать &lt;p&gt;&#8230;&lt;/p&gt; с содержимым в нужных местах, а в стилях для абзацев назначить <strong>clear:both</strong>, то можно избежать ненужного мусора в коде.</p>
<p>Использование ссылок и «системных спэнов». Не очень хорошая практика: вешать <strong>onClick</strong> на ссылку, которая никуда не ведёт. С точки зрения интерфейса — эффект обманутого ожидания, плюс некоторые пользователи смотрят в статусную строку или на всплывающую подсказку и видят, что со ссылкой что-то не то. Современная практика: делать span с особым классом, визуализирующийся как текст цвета ссылки, подчёркнутый прерывистой линией (<strong>dashed</strong>), чтобы было похоже на ссылку, но лишь отчасти. Это не правило, но серьёзная рекомендация. Для span можно использовать атрибут <strong>title</strong>, который будет выполнять роль всплывающей подсказки.</p>
<p>Запрет кэширования. В большинстве случаев достаточно пары тэгов:</p>
<pre>&lt;meta http-equiv="Pragma" content="no-cache"&gt;</pre>
<pre>&lt;meta http-equiv="Expires" content="0"&gt;</pre>
<p>Хотя бы на время отладки приложений.</p>
<p>При выделении (эмфазисе) фрагментов текста вместо тэгов <strong>&lt;b&gt;</strong> и <strong>&lt;i&gt;</strong> используем <strong>&lt;strong&gt;</strong> и <strong>&lt;em&gt;</strong>, если это действительно семантически оправдано и нет смысла делать спэны с классами. Такая замена соответствует спецификации XHTML и будущим стандартам HTML 5 (отказ от визуального форматирования в пользу логико-семантического).</p>
<p>Если используем таблицы, избавляемся от <strong>&lt;thead&gt;</strong> и <strong>&lt;tbody&gt;</strong>, если вдруг они невзначай генерируются редактором. Это устаревшие элементы.</p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/03/16/168/feed</wfw:commentRss>
		</item>
		<item>
		<title>Пример JavaScript-компонента с использованием ООП</title>
		<link>http://lar.ru/blog/2009/01/08/137</link>
		<comments>http://lar.ru/blog/2009/01/08/137#comments</comments>
		<pubDate>Thu, 08 Jan 2009 14:48:03 +0000</pubDate>
		<dc:creator>aracus</dc:creator>
		
		<category><![CDATA[Делимся опытом]]></category>

		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=137</guid>
		<description><![CDATA[Автор: Анна Черножукова
После того, как мы ознакомились с классами в Motools, можно (и нужно) рассмотреть конкретный пример.
Пусть на странице есть div, внутри него текст, который не должен превышать 20px в высоту. Если текст не умещается в эту высоту, то его нужно скрыть до нужной высоты и в конце видимой части текста добавить ссылку «&#8230;more». Щелкаем [...]]]></description>
			<content:encoded><![CDATA[<p><em>Автор: Анна Черножукова</em></p>
<p>После того, как мы <a href="/blog/2008/12/01/44">ознакомились</a> с классами в <a href="http://mootools.net" target="_blank">Motools</a>, можно (и нужно) рассмотреть конкретный пример.<br />
Пусть на странице есть <code>div</code>, внутри него текст, который не должен превышать <code>20px</code> в высоту. Если текст не умещается в эту высоту, то его нужно скрыть до нужной высоты и в конце видимой части текста добавить ссылку «&#8230;more». Щелкаем по ссылке —  видим текст целиком.<br />
<span id="more-137"></span><br />
Итак, на странице есть нужный <code>div</code>:</p>
<p><code>&lt;div id="detlongDescription" style="width:500px"&gt;За  последние  десятилетия  интерес к артистам, практикующим  искусство голодания,  сильно упал. И если раньше  было очень даже  выгодно  устраивать крупные представления подобного рода за собственные средства, то сегодня это совершенно невозможно. Тогда были другие времена.&lt;/div&gt;</code></p>
<p>Напишем mootools-класс, который будет получать в конструктор <code>id</code> того блока, в котором находится текст и обрабатывать его.</p>
<p>Приступим теперь к реализации компонента. Создаем свой класс, назовем его <code>SLDescription</code>. Как это будет работать:<br />
1. Создается экземпляр класса: вызывается конструктор <code>initialize</code>, куда передаем <code>id</code> блока с нужным текстом. Так же в конструкторе инициализируются дополнительные свойства класса: высота блока (у нас это <code>20px</code>), которую не должен превышать <code>div</code> с текстом, и текст ссылки на полный вариант текста.<br />
2. На загрузку страницы у созданного объекта вызывается метод <code>start()</code>. Здесь происходит следующее:<br />
— определяются координаты блока с текстом на странице и проверяется, превышает ли он допустимую высоту (ту, что определили в конструкторе);<br />
— если не превышает, то ничего не делаем;<br />
— если блок превышает указанную высоту, то сохраняем полный текст в некоторое поле объекта, также разбиваем текст внутри блока на слова и храним эти слова в виде массива. Затем содержимое блока очищается. Далее сокращенный вариант текста собирается по одному слову до достижения нужной высоты. Здесь есть одна особенность. Нам нужно, чтобы в нужную высоту вписался часть текста вместе со ссылкой на полный вариант, поэтому вначале к <code>div</code>’у добавляется новое слово + название ссылки. Затем проверяется, не достигли ли мы нужной высоты. Если достигли, то на этом выходим из цикла по массиву слов, а если не достигли, то добавляем только слово из массива к сокращенному тексту (не забываем, что кроме слов нужно восстанавливать пробелы между ними).<br />
Выйдя из цикла, помещаем внутрь <code>div</code>’a сокращенный текст + ссылку.</p>
<p>Реализуем сначала упрощенный вариант нашего алгоритма. Считаем, что не нужна нам пока никакая ссылка и никакой полный вариант текста тоже пока не нужен, а будем добавлять в конце сокращенного текста только надпись «&#8230;more».  Итак, приступаем к написанию класса:</p>
<pre><code>var SLDescription = new Class({
	initialize: function(divId) {
		//задаем свойства объекта
		this.divId=divId;
		//задаем высоту блока с текстом
		this.baseHeight=20;
		this.moreText = "...more";
},
start:function() {
        	var coordinates=null;
		//получаем фактические координаты div'а
        	coordinates= $(this.divId).getCoordinates();
		//разделяем текст на массив слов
		var splitText=$(this.divId).innerHTML.split(' ');
		$(this.divId).innerHTML='';
		var limit=true;
		var i=0; //количество показанных слов
		var text="";
		this.shortText="";
		//собираем сокращенный вариант текста по словам
		for( i=0; ithis.baseHeight){
				limit=false;
			// превысили высоту div'а, поэтому заканчиваем цикл
			}
			else {
				this.shortText=text;
			}
		}
		// помещаем сокращенный текст внутрь элемента
		$(this.divId).innerHTML=this.shortText;
		// если закончили текст раньше,
		// чем закончились слова в массиве,
		// то добавляем к тексту еще надпись
		if (i&lt;splitText.length)
			$(this.divId).innerHTML+=this.moreText;
	}
}).</code></pre>
<p><a href="/box/samples/fex_simple.html" target="_blank">Посмотреть, как работает</a>.</p>
<p>До сих пор нигде не встречалась конструкция <code>$(...)</code>. На самом деле выражение <code>$('elemId')</code> в Mootools возвращает ссылку на элемент в DOM, у которого <code>id='elemId'</code>. Это тоже самое, что и <code>document.getElementById('elemId')</code>. Кроме того, еще мы вызываем такие методы у элемента, как <code>getStyle(...)</code> и  <code>getCoordinates(...)</code>, но останавливаться подробно на них не будем, т.к. очевидно, что они делают. Более подробные сведения и описание вообще всех методов элементов в Mootools можно найти на <a href="http://mootools.net/docs/Element/Element" target="_blank">сайте с документацией по Mootools</a>.</p>
<p>Теперь усложним наш класс. Будем теперь добавлять в конце текста именно ссылку, при щелчке по которой будет появляться весь текст целиком. В предыдущем варианте мы нигде не сохраняли первоначальный текст, т.к. не требовалось его показывать на странице. Теперь же прежде, чем разбивать текст на слова и выполнять дальнейшие действие, необходимо сохранить текст в его первоначальном виде. Для этого немного исправим метод <code>start</code>, заменив<br />
<code>...<br />
var splitText=$(this.divId).innerHTML.split(' ');<br />
...</code><br />
на<br />
<code>...<br />
this.longText=$(this.divId).innerHTML;<br />
var splitText=this.lingText.split(' ');<br />
...</code></p>
<p>Также вместо<br />
<code>...<br />
if (i&lt;splitText.length)<br />
$(this.divId).innerHTML+=this.moreText;<br />
...</code><br />
появится<br />
<code>...<br />
if (i&lt;splitText.length)<br />
this.addMore();<br />
...</code></p>
<p>Теперь вместо того, чтобы просто добавить в конце текста только надпист «&#8230;more», будет вызываться метод, добавляющий ссылку «&#8230;more» на полный текст. Дописываем к нашему классу метод <code>addMore</code>:<br />
<code></p>
<pre>...
addMore:function(){
	//создаем новый элемент в DOM
		var a = new Element('a', {
		    href: "javascript:;"
		});
		//добавляем событие к элементу
		a.addEvent('click', this.more.bind(this));
		//добавляем название ссылки
		a.appendText(this.moreText);
		//вставляем элемент внурь div'а
		a.injectInside($(this.divId));
	},
	...</pre>
<p></code></p>
<p>Здесь поясним несколько моментов. Сначала мы с помощью специального объекта Mootools <a href="http://mootools.net/docs/Element/Element#Element:constructor" target="_blank">создаем новый элемент</a> в DOM — ссылку, и указываем ее атрибут <code>href</code>. Затем, также с помощью функций Mootools, мы <a href="http://mootools.net/docs/Element/Element.Event#Element:addEvent" target="_blank">добавляем к созданному элементу событие</a> <code>onclick</code>. Вообще метод <code>addEvent</code> принимает 2 параметра: тип события — строка с названием события без приставки <code>on</code>, и функция, вызываемая на это событие. В нашем случае, при нажатии на ссылку будет вызываться функция <code>more()</code> (её описание см. ниже), в которой  переменная <code>this</code> будет ссылаться не на ссылку, а на экземпляр нашего класса <code>SLDescription</code>.</p>
<p>Поясним еще как работает метод <code>injectInside(element)</code>. В нашем случае этот метод вставляет ссылку внутрь элемента с указанным <code>id</code>. Ссылка при этом допишется в конец содержимого <code>element</code>.</p>
<p>Наконец добавляем последний метод, который возвращает первоначальный текст (та самая функция, которая вызывается по щелчку на ссылке)</p>
<pre><code>...
	more:function(){
		$(this.divId).innerHTML=this.longText;
	}

}) //закрывающие скобки для всего класса</code></pre>
<p><strong>Замечание:</strong> Рассмотрим такую ситуацию</p>
<pre><code>			element.addEvent('click', fn1);
			...
			element.addEvent('click', fn2);</code></pre>
<p>Возникает вопрос, какая функция вызовется на событие <code>onclick</code>. Правильный ответ — обе, т.к. события <code>addEvent</code> добавляет функцию к уже имеющимся, которые будут вызываться на обработку события.</p>
<p>Добавляем на страницу ссылку на скрипт, создание объекта и <code>SLDescription</code> и вызов у него метода <code>start()</code>:</p>
<pre><code>&lt;script type="text/javascript" src="mootools.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript" src="text_description.js"&gt;&lt;/script&gt;
&lt;script type="text/javascript"&gt;
	var desc=new SLDescription("detlongDescription");
	window.addEvent('load', function(){
		desc.start();
	});
&lt;/script&gt;</code></pre>
<p>Готово. Теперь все заработает так, как задумали вначале.</p>
<p>Полный текст скрипта <span class="pseudolink" onclick="opCl('ex1')">text_description.js</span>.</p>
<div id="ex1" style="display:none;">
<pre><code>
var SLDescription = new Class({
initialize: function(divId) {
		//задаем свойства объекта
		this.divId=divId;
		//задаем высоту блока с текстом
		this.baseHeight=20;
		this.moreText = "...more";
},
start:function() {
        	var coordinates=null;
		//получаем фактические координаты div'а
        	coordinates= $(this.divId).getCoordinates();
		//сохраняем первоначальный текст
		this.longText = $(this.divId).innerHTML;
		//разделяем текст на массив слов
		var splitText=this.longText.split(' ');
		$(this.divId).innerHTML='';
		var limit=true;
		var i=0; //количество показанных слов
		var text="";
		this.shortText="";
		//собираем сокращенный вариант текста по словам
		for( i=0; i&lt;splitText.length&amp;&amp;limit; i++ ) {
			text+=(splitText[i]+" ");
			//текст должен укладываться в нужную высоту вместе со
			//ссылкой, поэтому перед проверкой
			//добавляем её вместе с текстом в div
			$(this.divId).innerHTML=(text+this.moreText);
			coordinates= $(this.divId).getCoordinates();
			if(coordinates.height&gt;this.baseHeight){
				limit=false;//превысили высоту div'а, поэтому выходим из цикла
			}
			else {
				this.shortText=text;
			}
		}
		//помещаем сокращенный текст внутрь элемента
		$(this.divId).innerHTML=this.shortText;
		//если закончили текст раньше, чем закончились слова в массиве,
		//то добавляем к тесту ссылку
		if (i&lt;splitText.length)
			this.addMore();
	},	

addMore:function(){
		//создаем новый элемент в DOM
		var a = new Element('a', {
		    href: "javascript:;"
		});
		//добавляем событие к элементу
		a.addEvent('click', this.more.bind(this));
		//добавляем текст, фактически это название ссылки
		a.appendText(this.moreText);
		//вставляем элемент внурь div'а
		a.injectInside($(this.divId));
	},

	//получить весь текст целиком
more:function(){
		$(this.divId).innerHTML=this.longText;
	}

});
</code></pre>
</div>
<p><a href="/box/samples/fex.html" target="_blank">Посмотреть, как работает</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/01/08/137/feed</wfw:commentRss>
		</item>
		<item>
		<title>C праздниками!</title>
		<link>http://lar.ru/blog/2009/01/05/133</link>
		<comments>http://lar.ru/blog/2009/01/05/133#comments</comments>
		<pubDate>Mon, 05 Jan 2009 06:30:17 +0000</pubDate>
		<dc:creator>Erlang</dc:creator>
		
		<category><![CDATA[Организационное]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=133</guid>
		<description><![CDATA[Компания «ЛАР» сердечно поздравляет читателей блога с новогодними праздниками!
В 2009 году на сайте вас ждёт много интересных материалов, часть из них — в новом формате. Обновления коснутся и самой компании. Внимательно следите за новостями!
]]></description>
			<content:encoded><![CDATA[<p>Компания «ЛАР» сердечно поздравляет читателей блога с новогодними праздниками!<br />
В 2009 году на сайте вас ждёт много интересных материалов, часть из них — в новом формате. Обновления коснутся и самой компании. Внимательно следите за новостями!</p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2009/01/05/133/feed</wfw:commentRss>
		</item>
		<item>
		<title>Занятие 3. Сборка страниц: Tiles</title>
		<link>http://lar.ru/blog/2008/12/24/119</link>
		<comments>http://lar.ru/blog/2008/12/24/119#comments</comments>
		<pubDate>Wed, 24 Dec 2008 07:06:53 +0000</pubDate>
		<dc:creator>Erlang</dc:creator>
		
		<category><![CDATA[Учебный курс]]></category>

		<category><![CDATA[Java]]></category>

		<category><![CDATA[Struts]]></category>

		<guid isPermaLink="false">http://lar.ru/blog/?p=119</guid>
		<description><![CDATA[Очень часто мы используем на разных страницах сайтов повторяющийся код. Это может быть навигационное меню, рекламные блоки, стандартные формы поиска или входа на сайт. Такие фрагменты могут повторяться на множестве страниц, а иногда и на всех. Было бы нерационально на каждую страницу включать одинаковый код, который теоретически может измениться (например, в меню добавляется один пункт, [...]]]></description>
			<content:encoded><![CDATA[<p>Очень часто мы используем на разных страницах сайтов повторяющийся код. Это может быть навигационное меню, рекламные блоки, стандартные формы поиска или входа на сайт. Такие фрагменты могут повторяться на множестве страниц, а иногда и на всех. Было бы нерационально на каждую страницу включать одинаковый код, который теоретически может измениться (например, в меню добавляется один пункт, следовательно, мы вынуждены корректировать код меню на каждой странице).<br />
Любая серверная технология позволяет «собирать» страницы по фрагментам, чтобы не писать повторяющийся код много раз. То есть многократно используемый код выносится в отдельный файл, а затем в нужном месте включается в основной файл. Фреймворк Struts 2 предоставляет аналогичную возможность, которая реализована при помощи технологии Tiles («черепица»). Поясним на конкретном примере (см. <a href="/box/illustrations/struts2_tiles.png" target="_blank">иллюстрацию</a>).<br />
<span id="more-119"></span><br />
На разных страницах используется форма входа на сайт: поля для логина и пароля и кнопка <code>Submit</code>. На другой части страниц, не видимых неавторизованному пользователю, есть блок с информацией о пользователе. Располагаются же они на одном и том же месте, поскольку никогда не встречаются вместе. Очевидно, что нет смысла повторять код с формой или информацию о пользователе в разных шаблонах: достаточно создать два файла, которые будут подключаться в зависимости от каких-то условий. Условие следующее: главная страница и страница с формой регистрации видны неавторизованным пользователям, при запросе же остальных страниц (экшнов) Struts перенаправляет браузер на страницу с формой входа (главную, например, <code>login.action</code>).<br />
Мы создаём два файла: один с формой входа, второй с обработкой сценария, запрашивающего данные о пользователе. В основном шаблоне определяем место для включения одного из этих файлов. Затем при обработке экшнов в зависимости от результата используется то или иное включение в основной шаблон.<br />
В основном шаблоне находится основная вёрстка — неизменные фрагменты кода — и указания (Struts-тэги) позиций, которые будут заполняться содержимым конкретных файлов. В конфигурационном файле <em>tiles.xml</em> для каждого конкретного экшна описывается заполнение этих позиций файлами. (Для каждого экшна есть набор описаний: какие позиции заполняются какими файлами. Если же описание пропущено, то вступают в силу описания, заданные как правила по умолчанию.) При обращении к тому или иному экшну производится серверная замена Tiles-тэга на результат включения указанного файла. В итоге окончательный файл собирается, как конструктор или черепица, но в зависимости от правил, указанных в <em>tiles.xml</em>. А то, что интерпретатор кода должен обращаться к файлу <em>tiles.xml</em>, косвенно указано в конфигурационном файле <em>struts.xml</em>. Рассмотрим процесс подробнее.<br />
Сервер обращается к файлу <em>struts.xml</em>. Его содержание по сравнению с предыдущими занятиями изменено (приводится фрагмент):</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration
2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"&gt;
&lt;struts&gt;
&lt;package name="common" extends="tiles-default"&gt;
  &lt;global-results&gt;
    &lt;result type="redirect" name="redirectToLogin"&gt;
      login.action&lt;/result&gt;
  &lt;/global-results&gt;
  &lt;action name="login" class="mymovieteach.action.LoginAction"
   method="input"&gt;
    &lt;result type="tiles"&gt;login&lt;/result&gt;
  &lt;/action&gt;
  &lt;action name="loginSubmit" class="mymovieteach.action.LoginAction"
   method="loginSubmit"&gt;
    &lt;result type="tiles" name="input"&gt;login&lt;/result&gt;
    &lt;result type="redirect"&gt;home.action&lt;/result&gt;
  &lt;/action&gt;
  &lt;action name="home" class="mymovieteach.action.HomeAction"
   method="show"&gt;
   &lt;result type="tiles"&gt;home&lt;/result&gt;
  &lt;/action&gt;
&lt;/package&gt;
&lt;/struts&gt;</code></pre>
<p>Строка <code>&lt;package name="common" extends="tiles-default"&gt;</code> указывает на то, что для обработки экшнов используется пакет (package) <code>common</code>, наследующий функциональность <code>tiles-default</code>.<br />
Благодаря такому наследованию в качестве типа результата можно указать значение <code>tiles</code>.<br />
Рассмотрим пример. Происходит обращение к экшну <code>login</code> (детально процесс рассмотрен в предыдущих занятиях):</p>
<pre><code>&lt;action name="login" class="mymovieteach.action.LoginAction"
 method="input"&gt;
   &lt;result type="tiles"&gt;login&lt;/result&gt;
&lt;/action&gt;</code></pre>
<p>Выполняется метод <code>input</code> класса <code>LoginAction</code> (как мы помним, метод <code>input</code> не требует валидации); он возвращает <code>SUCCESS</code>, а значит, используется результат по умолчанию (он же единственный для данного экшна). Тип результата — <code>tiles</code>; значение — <code>login</code>.<br />
В файле <em>tiles.xml</em> (приведен фрагмент) —</p>
<pre><code>&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
&lt;!DOCTYPE tiles-definitions PUBLIC
        "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
        "http://tiles.apache.org/dtds/tiles-config_2_0.dtd"&gt;
&lt;tiles-definitions&gt;
  &lt;definition name="defaultLayout" template="/WEB-INF/jsp/layout/default.jsp"&gt;
    &lt;put-attribute name="title" value=""/&gt;
    &lt;put-attribute name="leftTopPanel" value="/WEB-INF/jsp/inc/def_left_top_panel.jsp"/&gt;
    &lt;put-attribute name="leftBottomPanel" value=""/&gt;
    &lt;put-attribute name="rightTopPanel" value=""/&gt;
    &lt;put-attribute name="rightBottomPanel" value="/WEB-INF/jsp/inc/find_form_panel.jsp"/&gt;
  &lt;/definition&gt;
  &lt;definition name="login" extends="defaultLayout"&gt;
    &lt;put-attribute name="title" value="Login"/&gt;
    &lt;put-attribute name="leftBottomPanel" value="/WEB-INF/jsp/inc/join_left_bottom_panel.jsp"/&gt;
    &lt;put-attribute name="rightTopPanel" value="/WEB-INF/jsp/login_form.jsp"/&gt;
  &lt;/definition&gt;
  &lt;definition name="home" extends="defaultLayout"&gt;
    &lt;put-attribute name="title" value="Home"/&gt;
    &lt;put-attribute name="leftBottomPanel" value="/WEB-INF/jsp/inc/home_left_bottom_panel.jsp"/&gt;
    &lt;put-attribute name="rightTopPanel" value="/WEB-INF/jsp/inc/welcome_panel.jsp"/&gt;
  &lt;/definition&gt;
&lt;/tiles-definitions&gt;</code></pre>
<p>— содержатся строки, отвечающие за обработку данного типа результата:</p>
<pre><code>&lt;definition name="login" extends="defaultLayout"&gt;
  &lt;put-attribute name="title" value="Login"/&gt;
  &lt;put-attribute name="leftBottomPanel" value="/WEB-INF/jsp/inc/join_left_bottom_panel.jsp"/&gt;
  &lt;put-attribute name="rightTopPanel" value="/WEB-INF/jsp/login_form.jsp"/&gt;
&lt;/definition&gt;</code></pre>
<p>В этом фрагменте видно, что с помощью определений описываются три Tiles (<code>title</code>, <code>leftBottomPanel</code> и <code>rightTopPanel</code>). Но это описание наследует более общее описание Tiles по умолчанию (<code>extends="defaultLayout"</code>), в котором присутствуют пять атрибутов:</p>
<pre><code>&lt;definition name="defaultLayout" template="/WEB-INF/jsp/layout/default.jsp"&gt;
  &lt;put-attribute name="title" value=""/&gt;
  &lt;put-attribute name="leftTopPanel" value="/WEB-INF/jsp/inc/def_left_top_panel.jsp"/&gt;
  &lt;put-attribute name="leftBottomPanel" value=""/&gt;
  &lt;put-attribute name="rightTopPanel" value=""/&gt;
  &lt;put-attribute name="rightBottomPanel" value="/WEB-INF/jsp/inc/find_form_panel.jsp"/&gt;
&lt;/definition&gt; </code></pre>
<p>Однако часть из них не заполнена значениями. Это значит, что для каждого конкретного экшна необходимо назначить значения для этих атрибутов (что и происходит в описании Tiles для экшна <code>login</code>). В итоге, если восстановить все значения, которые наследуются, то получится следующий набор значений:</p>
<pre><code>&lt;definition name="login" template="/WEB-INF/jsp/layout/default.jsp"&gt;
  &lt;put-attribute name="title" value="Login"/&gt;
  &lt;put-attribute name="leftTopPanel" value="/WEB-INF/jsp/inc/def_left_top_panel.jsp"/&gt;
  &lt;put-attribute name="leftBottomPanel" value="/WEB-INF/jsp/inc/join_left_bottom_panel.jsp"/&gt;
  &lt;put-attribute name="rightTopPanel" value="/WEB-INF/jsp/login_form.jsp"/&gt;
  &lt;put-attribute name="rightBottomPanel" value="/WEB-INF/jsp/inc/find_form_panel.jsp"/&gt;
&lt;/definition&gt;</code></pre>
<p>Видно, что в качестве основного шаблона используется файл <code>/WEB-INF/jsp/layout/default.jsp</code>:</p>
<pre><code>&lt;%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %&gt;
&lt;%@ taglib prefix="s" uri="/struts-tags" %&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;&lt;tiles:insertAttribute name="title"/&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div id="general"&gt;
    &lt;div id="mainboard" class="mainpage"&gt;
      &lt;tiles:insertAttribute name="leftTopPanel"/&gt;
      &lt;tiles:insertAttribute name="leftBottomPanel"/&gt;
    &lt;/div&gt;
    &lt;div id="dashboard"&gt;
      &lt;tiles:insertAttribute name="rightTopPanel"/&gt;
      &lt;tiles:insertAttribute name="rightBottomPanel"/&gt;
    &lt;/div&gt;
    &lt;div id="footerbar"&gt;
      © &lt;a href="http://www.magicwebsolutions.co.uk" target="_blank"&gt;
           Magicwebsolutions.co.uk&lt;/a&gt;
      | &lt;a href="/terms.action"&gt;Terms and Conditions&lt;/a&gt;
      | &lt;a href="/privacy.action"&gt;Privacy Policy&lt;/a&gt;
      | &lt;a href="/bugreports.action"&gt;Reports and Comments&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>В нём содержатся:<br />
1. Включения программных библиотек для обработки Struts-тэгов и Tiles.<br />
2. Основная вёрстка (те фрагменты кода, которые не изменяются никогда, например, основные тэги и включения стилей и скриптов).<br />
3. Struts-тэги для вызова серверных включений файлов или значений.</p>
<p>Третий пункт наиболее важен для рассмативаемой темы.<br />
Синтаксис включения Tiles таков: <code>&lt;префикс:insertAttribute name="имя"/&gt;</code>. Префикс указан в самом начале файла в директиве <code>&lt;%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %&gt;</code> — это значит, что в данном документе вид Tiles-вызовов будет таким: <code>&lt;tiles:insertAttribute name="имя"/&gt;</code>. В качестве имени указываются те имена атрибутов, которые использованы в файле <em>tiles.xml</em>. То есть если мы встречаем вызов <code>&lt;tiles:insertAttribute name="title"/&gt;</code>, то будет использовано значение, указанное в <code>&lt;put-attribute name="title" value="Значение"/&gt;</code> в определении того экшна, который актуален в данный момент.<br />
Если описать это в виде процесса: запрашивается экшн, данные передаются серверу, в <em>struts.xml</em> описывается, что нужно сделать с данными (какой метод какого класса вызывается), экземпляр класса создаётся и заполняется данными, в случае успешного прохождения валидации (при необходимости) выполняется указанный метод, а в зависимости от того, что возвращается, используется тот или иной результат для данного экшна; в качестве результата, если его тип — <code>tiles</code>, — может быть указано имя для вызова Tiles; по этому имени из <em>tiles.xml</em> используется нужное определение (<code>definition</code>).<br />
Внутри определений есть наборы тэгов <code>&lt;put-attribute name="имя" value="значение"/&gt;</code>, из которых и будет производиться выбор во время вызова <code>&lt;префикс:insertAttribute name="имя"/&gt;</code>. В зависимости от значения тэга <code>&lt;put-attribute name="имя" value="значение"/&gt;</code> и типа этого значения и будет производиться замена <code>&lt;префикс:insertAttribute name="имя"/&gt;</code> на конкретное содержимое внутри основного шаблона, который указан в Tiles-определении как шаблон по умолчанию: <code>&lt;definition name="defaultLayout" template="/WEB-INF/jsp/layout/default.jsp"&gt;</code>, то есть файл <em>/WEB-INF/jsp/layout/default.jsp</em>.<br />
С точки зрения вложенности шаблонов есть несколько файлов, иерархия которых определяется в <em>tiles.xml</em>, а конкретные случаи включений описываются с помощью <em>struts.xml</em>.<br />
Но с помощью Tiles можно не только собирать страницы из файлов-фрагментов. Можно, например, заполнять вызовы простыми строковыми значениями. В тэгах <code>&lt;put-attribute name="имя" value="значение"/&gt;</code> можно указывать тип передаваемого значения: <code>&lt;put-attribute name="имя" value="значение" type="тип"/&gt;</code>. Типов четыре: <code>string</code> (строка), <code>template</code> (включаемый файл-шаблон), <code>definition</code> (вызов уже описанного определения) или <code>object</code> (объект). На самом деле тип указывать необязательно: Struts в большинстве случаев определяет тип корректно. В качестве строки можно указать обычное слово (как и происходит в случае с <code>&lt;put-attribute name="title" value="Login"/&gt;</code>), а если в начале значения стоит слэш (как в <code>&lt;put-attribute name="rightBottomPanel" value="/WEB-INF/jsp/inc/find_form_panel.jsp"/&gt;</code>), то Struts определяет тип данных как <code>template</code> и ищет ресурс по указанному пути, а если не находит, то выдаёт предупреждение. В нашем примере используются типы <code>string</code> и <code>template</code>.</p>
<p><a href="http://lar.ru/blog/wp-content/uploads/lessons/lesson3_src.zip">Исходный код к уроку</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lar.ru/blog/2008/12/24/119/feed</wfw:commentRss>
		</item>
	</channel>
</rss>
