[room] Лекция по Java

Eugene Prokopiev =?iso-8859-1?q?prokopiev_=CE=C1_stc=2Edonpac=2Eru?=
Ср Окт 25 14:43:40 MSD 2006


> Что следует изучать первое, для возможности запуска приложений с
> минимальными требованиями к конфигурации сервера (то есть либо CGI, либо,
> если это требует какого-либо модуля апача -- то это то что есть в Сизифе)?

Первое: пусть польют меня грязью, но я в данном вопросе сторонник 
недистрибутивного подхода, из Сизифа меня интересует только JDK/JRE. 
Обоснование: продукт должен работать не только в ALT, и не только в 
Linux (даже в качестве IDE я использую Eclipse в Linux, но со мной 
работает один человек в Windows и один еще не определился :) ). Более 
того, мне хочется, чтобы он собирался везде, где нет ничего, кроме JDK. 
Поэтому мне пока проще таскать все необходимые библиотеки с собой, они у 
меня лежат прямо в CVS вместе с исходниками. Я задницей чувствую, что 
когда-нибудь это начнет меня напрягать, когда потребуется гарантировать 
одинаковые версии библиотек в разных проектах, и тогда я буду смотреть 
на maven, а пока мне достаточно ant. Грубые аналогии: ant - это make, 
maven - это hasher/spt :)

Далее: CGI в виде отдельных процессов на каждый запрос в Java никто в 
здравом уме делать не станет - это слишком дорого. Основа всех 
технологий - это сервлеты, которые выглядят так:

public class Hello extends HttpServlet {

     public void doGet(HttpServletRequest request,
                       HttpServletResponse response)
         throws IOException, ServletException {

	response.setContentType("text/html");
	PrintWriter writer = response.getWriter();

	writer.println("<html>");
	writer.println("<head>");
	writer.println("<title>Sample Application Servlet Page</title>");
	writer.println("</head>");
	writer.println("<body bgcolor=white>");
	writer.println("</body>");
	writer.println("</html>");

     }

}

Принцип ясен?

Есть надстройки над этим - template engines типа JSP и Velocity, которые 
позволяют писать в стиле PHP, т.е. html + вставки кода. Кстати, Velocity 
как template engine используется в проектах, которые к web никаким боком.

Есть надстройки над этими надстройками :) Есть другие надстройки над 
сервлетами. Начать читать можно отсюда - 
http://www.techinfo.net.ru/docs/web/javawebdev.html

Об инфраструктуре: минимальное web-приложение выглядит так:

$ ls -lR
.:
итого 24
-rwxr-xr-x  1 john john  296 Фев 19  2006 build.sh
-rw-r--r--  1 john john 2124 Окт 25 13:23 build.xml
drwxr-xr-x  2 john john 4096 Окт 25 13:24 docs
drwxr-xr-x  2 john john 4096 Окт 25 13:21 lib-build
drwxr-xr-x  3 john john 4096 Янв 25  2005 src
drwxr-xr-x  4 john john 4096 Янв 25  2005 web

./docs:
итого 4
-rw-r--r--  1 john john 62 Авг 28  2004 README.txt

./lib-build:
итого 1556
-rw-r--r--  1 john john 999966 Фев 19  2006 ant.jar
-rw-r--r--  1 john john 407573 Фев 19  2006 ant-nodeps.jar
-rw-rw-rw-  1 john john  31642 Авг 13  2005 commons-logging.jar
-rw-r--r--  1 john john 132263 Фев 19  2006 javax.servlet.jar

./src:
итого 4
drwxr-xr-x  2 john john 4096 Авг 28  2004 mypackage

./src/mypackage:
итого 4
-rw-r--r--  1 john john 3165 Авг 28  2004 Hello.java

./web:
итого 16
-rw-r--r--  1 john john 1019 Авг 28  2004 hello.jsp
drwxr-xr-x  2 john john 4096 Авг 28  2004 images
-rw-r--r--  1 john john  636 Авг 28  2004 index.html
drwxr-xr-x  3 john john 4096 Окт 25 13:24 WEB-INF

./web/images:
итого 4
-rw-r--r--  1 john john 1441 Авг 28  2004 tomcat.gif

./web/WEB-INF:
итого 8
drwxr-xr-x  2 john john 4096 Янв 25  2005 lib
-rw-r--r--  1 john john  684 Фев  6  2005 web.xml

./web/WEB-INF/lib:
итого 0

web/WEB-INF/web.xml - это дескриптор вида:

<!DOCTYPE web-app
     PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

     <display-name>Hello, World Application</display-name>
     <description>
         This is a simple web application with a source code organization
         based on the recommendations of the Application Developer's Guide.
     </description>

     <servlet>
         <servlet-name>HelloServlet</servlet-name>
         <servlet-class>mypackage.Hello</servlet-class>
     </servlet>

     <servlet-mapping>
         <servlet-name>HelloServlet</servlet-name>
         <url-pattern>/hello</url-pattern>
     </servlet-mapping>

</web-app>

Класс Hello мы уже рассматривали :)

build.xml (и вызывающий его build.sh) умеет строить из этого дерева 
файлов war-архив. Этот архив можно разными способами продеплоить в 
Tomcat: например, скопировать его в определенный каталог, который он 
мониторит на предмет появления новых приложений. Будет это приложение 
работать и в других контейнерах вроде Jetty и Resin, а также в тяжелых 
серверах приложений вроде JBoss и Geronimo, которые содержат встроенные 
сервлет-контейнеры (коими являются Tomcat и Jetty :) ).

Мне больше нравится Jetty, т.к. он легче, меньше, проще, понятнее ... 
Взять маленький Jetty можно тут - 
http://lib.juga.ru/article/articleview/222/1/0?PrintableVersion=enabled

Т.е. Apache web server здесь отдыхает. Его подставляют как frontend. 
Можно ли задействовать nginx - не в курсе.

Архив приложения ушел в личку ...

Но это все классика.

Если хочется странного (FastCGI), то пишется обычное java-приложение, 
которое обменивается с web-сервером через сокеты.

Такое приложение можно писать, забыв о наличии каких-либо frameworks. Но 
если я знаю, что будет много относительно независимых и взамозаменямых 
модулей (и я узнаю о том, какие конфигурации будут нужны, только на 
этапе внедрения), то я задействую Spring.

Стартовый класс:

public class Main {

	public static void main(String[] args) throws InterruptedException {
		
		if (args.length > 0) {
			final AbstractApplicationContext ctx =
				new FileSystemXmlApplicationContext(new String []{args[0]});
			ctx.registerShutdownHook();
			A a = (A)ctx.getBean("a");
			a.doSomething();
			Thread.sleep(Long.MAX_VALUE);
		} else {
			System.err.println("Usage : java mypackage.Main <context.xml>");
             System.exit(-1);
		}
	}

}

В нем я загружаю все мои модули из внешнего конфигурационного файла 
(контекста в терминах Spring), а затем получаю ссылку на модуль а и 
вызываю один из его методов. Вывод на консоль после запуска java 
mypackage.Main conf/beans.xml:

INFO  CollectionFactory                - JDK 1.4+ collections available
INFO  XmlBeanDefinitionReader          - Loading XML bean definitions 
from file [/home/john/workspace/SpringApp/conf/beans.xml]
INFO  FileSystemXmlApplicationContext  - Bean factory for application 
context 
[org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=11850709]: 
org.springframework.beans.factory.support.DefaultListableBeanFactory 
defining beans [a,b]; root of BeanFactory hierarchy
INFO  FileSystemXmlApplicationContext  - 2 beans defined in application 
context 
[org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=11850709]
INFO  FileSystemXmlApplicationContext  - Unable to locate MessageSource 
with name 'messageSource': using default 
[org.springframework.context.support.DelegatingMessageSource на 173831b]
INFO  FileSystemXmlApplicationContext  - Unable to locate 
ApplicationEventMulticaster with name 'applicationEventMulticaster': 
using default 
[org.springframework.context.event.SimpleApplicationEventMulticaster на 13caecd]
INFO  DefaultListableBeanFactory       - Pre-instantiating singletons in 
factory 
[org.springframework.beans.factory.support.DefaultListableBeanFactory 
defining beans [a,b]; root of BeanFactory hierarchy]
INFO  B                                - init
INFO  A                                - init
INFO  B                                - do something

Затем жду, пока пользователь не нажмет Ctrl+C :) Вывод:

INFO  FileSystemXmlApplicationContext  - Closing application context 
[org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=11850709]
INFO  DefaultListableBeanFactory       - Destroying singletons in 
factory 
{org.springframework.beans.factory.support.DefaultListableBeanFactory 
defining beans [a,b]; root of BeanFactory hierarchy}
INFO  A                                - destroy
INFO  B                                - destroy

Конфигурационный файл со связями между модулями a и b выглядит так:

<beans>
	<bean id="a" class="mypackage.A" init-method="init" 
destroy-method="destroy">
		<property name="c" ref="b"/>
	</bean>
	<bean id="b" class="mypackage.B" init-method="init" 
destroy-method="destroy"/>
</beans>

Вот как выглядит класс B:

public class B implements C {	
	private Log log = LogFactory.getLog(getClass());
	public void init() {
		log.info("init");
	}
	public void destroy() {
		log.info("destroy");
	}
	public void doSomething() {
		log.info("do something");
	}
}

Он реализует интерфейс С:

public interface C {
	public void doSomething();
}

Который, в свою очередь, используется классом А:

public class A {	
	private Log log = LogFactory.getLog(getClass());
	private C c;
	public void setC(C c) {
		this.c = c;
	}
	public void init() {
		log.info("init");
	}
	public void destroy() {
		log.info("destroy");
	}
	public void doSomething() {
		c.doSomething();
	}
}

Удобство Spring в том, что он:

1) позволяет декларативно описать, кто кого и как должен вызвать, что с 
какими параметрами должно быть инициализировано - но тут реализация 
HiveMind может быть даже более изящна

2) подключить готовые модули, например, для доступа к БД и т.д - вот тут 
равных Spring'у нет

Рассмотрим последнее на примере задействования технологии JMX. Пусть в 
процессе работы приложения обслуживающий персонал должен вызывать 
зачем-то метод A.doSomething(). Мы описываем класс A как implements 
AMBean, последний выглядит так:

public interface AMBean {
	public void doSomething();
}

Теперь исправляем beans.xml:

<beans>
	
	<bean id="mbeanServer" 
class="org.springframework.jmx.support.MBeanServerFactoryBean"/>
     <bean id="exporter" 
class="org.springframework.jmx.export.MBeanExporter">
		<property name="beans">
         	<map>
             	<entry key="bean:name=A" value-ref="a"/>
             </map>
		</property>
		<property name="assembler">
			<bean 
class="org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler"/>
		</property>
         <property name="server" ref="mbeanServer"/>
	</bean>
	<bean id="myBean" class="MyBean"/>
     <bean id="serverConnector" 
class="org.springframework.jmx.support.ConnectorServerFactoryBean">
		<property name="objectName" value="connector:name=rmiConnector"/>
        	<property name="serviceUrl" 
value="service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi"/>
         <property name="threaded" value="true"/>
         <property name="daemon" value="true"/>
         <property name="server">
			<ref local="mbeanServer"/>
         </property>
	</bean>
	<bean id="registry" 
class="org.springframework.remoting.rmi.RmiRegistryFactoryBean">
		<property name="port" value="1099"/>
	</bean>
	
	<bean id="a" class="mypackage.A" init-method="init" 
destroy-method="destroy">
		<property name="c" ref="b"/>
	</bean>
	<bean id="b" class="mypackage.B" init-method="init" 
destroy-method="destroy"/>
	
</beans>

После запуска приложения запускаем jconsole, подключаемся к 
service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi без логина/пароля, 
находим в дереве наш класс и вызываем doSomething

Архив с проектом ушел в личку ...

Кто скажет, что это не изящно, пусть бросит в меня камень :)

Да, похоже я исполнил мечту г-на dlaygovta@ :)

Денис, если все это тебе еще интересно, то в качестве оплаты лекции 
прошу написанное причесать и куда нибудь на f.i. выложить :)

>>EP>> А если хочется "FastCGI, темплейтов и прочей радости, а также компилятор 
>>EP>> в неё с простого PHP-like язычка" - то бишь именно своего велосипеда - 
>>EP>> то вообще непонятно, что в PHP есть такого, что ты предпочел именно его 
>>EP>> ... К слову, компиляторов в байт-код JVM с различных функциональных и 
>>EP>> императивных языков довольно много
> 
> 
>>>Скорее уж проще самому налабать интерпретатор на той же жабе.
> 
> EP> Свой DSL? Возможно, есть смысл посмотреть в сторону antlr ...
> 
> Посмотрю.
> 
> Кстати о. Какие наиболее простые средства a-la flex/bison сейчас есть в
> Java?

кажется, именно antlr и есть

>>>PHP было выбрано как меньшее из зол. То бишь либо самому делать весь стек,
>>>либо не строить из себя идеалиста а просто сделать чтобы хорошо работало.
>>>Но я все равно ищу что-то куда свалить.
> 
> EP> :)
> EP> Прелесть Java не в языке. Язык средний, в частностях вроде интерфейсов и 
> EP> анонимных классов лучше предшественника (C++), а в некоторых более 
> EP> глобальных вопросах (класс String ;) ) просто потрясающий своей 
> EP> нелогичностью. Хотя привыкнуть можно :)
> 
> Меня String убивает именно тем, что код который на perl том же занимает
> несколько символов и понятен -- на Java получается простыня кода :)

В Java есть регулярные выражения - не помогут?

> EP> Прелесть именно в окружении. В наличии спецификаций, в конкурирующих и 
> EP> относительно совместимых реализаций этих спецификаций. В количестве 
> EP> библиотек (один jakarta.apache.org чего стоит). В средствах сборки типа 
> EP> ant/maven. В неплохих IDE вроде Eclipse и IDEA, хотя, конечно, для 
> EP> любителей vim/emacs это не аргумент :)
> 
> Другая проблема -- в отличии от perl, где фактически все в одной коробке,
> Java имеет больший порог вхождения чтобы знать какой из модулей наиболее
> применим для данной задачи. Именно из-за конкуренции.

Есть такая беда :)

Многие "левые" решения имеют тенденцию к помещению к коробку, правда, 
иногда в изуродованном виде, так что удобнее оказывается использовать 
то, что осталось вне коробки :) Яркий пример - механизмы 
протоколирования вроде встроенного, log4j и унифицированного commons logging

>>>Ну, в жабе в этом смысле все можно сделать красиво, если я правильно
>>>понял. Спрятать вообще доступ к SQL в объеты, реализовать для них
>>>интерфейсе serializable после чего сделать кэш. Вот во всяких PHP-подобных
>>>это уже оказывается куда тяжелее.
> 
> EP> А в Java даже есть готовое вроде JBoss Tree Cache :)
> 
> :)

У меня как-то был соблазн к нему подступиться, но потом понял, что узкое 
место было не здесь ...

-- 
С уважением, Прокопьев Евгений




Подробная информация о списке рассылки smoke-room