Дали ще бъдем модерни ако решим да използваме Java, Linux, JBoss, Apache Axis и Hibernate3?
Определено ДА. Наскоро ми се наложи да сглобя нещо такова за много кратко време.
Като разпитах се оказа, че това и често избирана комбинация при разработката на софтуер. Затова ще споделя моя опит и идеи.
Да се направи нещо такова:
Имаме някакъв уеб базирано приложение и когато се обърнем към него през уеб сървисите, които той предлага, взема нещо от базата дани и ни врща резултат.
Ама и аз какво описание дадох. Много общо го формулирах ама който се интересува ще му стане ясно за какво идва реч.
Ето какви мисли ме налегнаха, когато видях задачата и как си отговорих на тях.
Каква линукс дистрибуция да използвам?
Как да напрявя така че да променям приложението най-безболезнено?
Дали ще мога да генерирам част от кода?
и още един допълнителен: Какво ще ям довечера?
Първия въпрос го отсях веднага щом ми казаха, че имам пълна свобода за хостващата система.
Казах си, да бъде Debian. При Дебиан нещата с инсталирането на java са специфични. Няма .deb пакети затова човек сам трябва да си го направи. Ето как:
1. Като root си инсталирам нещата, които ми трябват за създаването на java пакета: # apt-get install java-package 2. Важно! "fakeroot" се използва за създаването на .deb # apt-get install fakeroot 3. Вече не съм root. 4. Изтеглям <SUN JAVA SDK FILENAME>.bin от сайта на Sun . Изтегляния файл го записвам в една temp директория. 5. Смело стартирам $ fakeroot make-jpkg <SUN JAVA SDK FILENAME>.bin 6. Инсталирам Java .deb Packagе вече като root: # dpkg -i <GENERATED DEB PACKAGE FILENAME>.deb 7. Проверявам какво съм направил и дали рабои $ java -version 5. Setup на JAVA_HOME, CLASSPATH и JBOSS_HOME в /etc/profile export JAVA_HOME=/usr/lib/j2sdk1.5-sun/ export CLASSPATH=. export JBOSS_HOME=/opt/jboss-4.0.4.GA
Подготвям се старателно и за JBOSS. Директорията /opt съм я отделил предвидливо в отделен patition. Създавам и user - jboss.
Логвам се като jboss свалям последната версия на сървъра и разархивирам в /opt. Да обаче като гледам как се пуска си викам, абе какво ще стане ако сървъра се рестартира? Някои трябва да го стартира автоматично всеки път. За production сървъра ще е добре jboss да се пуска като демон. Ето как става и това:
#! /bin/sh
# /etc/init.d/jboss
#
# NOTE: Points JBoss installation dir
JBOSS=/opt/jboss-4.0.4.GA
start(){
echo "Starting jboss..."
su -l jboss -c "$JBOSS/bin/run.sh > /dev/null 2> /dev/null &"
}
stop(){
echo "Stopping jboss..."
su -l jboss -c "$JBOSS/bin/shutdown.sh -S &"
}
restart(){
stop
sleep 60
# protect against any services that can't stop before we restart
# (warning this kills all Java instances running as 'jboss' user)
su -l jboss -c 'killall java'
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "Usage: /etc/init.d/jboss {start|stop|restart}"
exit 1
esac
exit 0
В Дебиан, за да сложиш една програма като демон съпките са следните:
# инсталираме Write this file as /etc/init.d/jboss chmod 755 /etc/init.d/jboss update-rc.d jboss defaults # ако искаме да го разкараме update-rc.d -f jboss remove
Имам си средата за исталация на приложението, време е да се заема със структурата на проекта. Това е и отговора на моя втори въпрос.
Искам всяко нещо да си знае мястото и ant скрипта да ми е максимално упростен. Да знам къде се намират генерираните файлове и къде е бизнес логиката. Мислих, мислих и ето какво измислих:
bin - тук се намират всички компилирани класове. Запомнете, при компилация всички конфигорационни файлове трябва да се копират тук(всичко от conf directory)! conf - конфигорационни файлове data (dbschema.sql)- sql който ще се генерира lib - всички нужни директории src (com.zlatozar.persistence, com.zlatozar.persistence.base, com.zlatozar.soap, com.zlatozar.common)- всички сорсове test (com.zlatozar...) - всички тестове. Запомнете, че src и test трябва да имат еднаква структура webcontent (html, jsp) - нещата за нашия сайт WEB-INF (web.xml, server-config.wsdd)- нужните конфигурации за сайта
При тази схема на проекта много лесно мога да генерирам нужния ми war файл и да пускам всички тестове. Идва най-интересната част за мен, настройката на Apache Axis. Нека първо да погледнем катинката и после е лесно - разказ по картинка.

Какви са минималните усилия за направата на Web Services(WS)? Пиша интерфейса, който искам да се вижда "навън" и пиша клас, които го имплементира всичко друго е Axis гимнастика. Давам пример:
package com.zlatozar.soap;
public interface SaySomething extends java.rmi.Remote {
public String sayHello(String name) throws java.rmi.RemoteException;
public String sayGoodbye() throws java.rmi.RemoteException;
public String writeSometingToDB(String id, String something) throws java.rmi.RemoteException;
}
ето и имплементацията:
package com.zlatozar.soap;
import java.rmi.RemoteException;
import com.zlatozar.persistence.QueryManager;
public class SaysomethingSoapBindingImpl implements SaySomething {
public String sayHello(String name) throws RemoteException {
return "hello " + name + " !";
}
public String sayGoodbye() throws RemoteException {
return "goodbye my master !";
}
public String writeSomethingToDB(String id, String something)
throws RemoteException {
Something ft = new SomethingBase(id, something);
QueryManager.saveTicketToDB(ft);
return "Done. Persistance is cool. Bye.";
}
// Utilties methods
}
Обаче тук има една важна особенност: пише се SaysomethingSoapBindingImpl, а не SaySomethingSoapBindingImpl. Гледаме картинката. От тези два класа искаме да получим всичко за "axis deployment"(server side). Тук е и тънката хватка. От тези два класа генерирам .wsdl файла после от него нещата които са ми необходими - SaysomethingSoapBindingSkeleton и server-config.wsdd. Вижте и ant scrip-a ми:
<!-- ================================ -->
<!-- Axis tools definiion -->
<!-- ================================ -->
<taskdef name="axis-java2wsdl" classname="org.apache.axis.tools.ant.wsdl.Java2WsdlAntTask">
<classpath refid="compile.classpath" />
</taskdef>
<taskdef name="axis-wsdl2java" classname="org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask">
<classpath refid="compile.classpath" />
</taskdef>
<!-- ================================ -->
<!-- Axis code generation -->
<!-- ================================ -->
<target name="export-wsdl" depends="prepare">
<axis-java2wsdl
classname="com.zlatozar.soap.SaySomething"
style="rpc"
classpathref="compile.classpath"
namespace="urn:soap.zlatozar.com"
location="http://${server.name}:8080/${project.distname}/soap/${project.distname}"
output="${basedir}\generated\${project.distname}.wsdl">
</axis-java2wsdl >
</target>
<target name="export-java" depends="export-wsdl">
<axis-wsdl2java
output="${basedir}\generated"
serverside="true"
skeletondeploy="true" testcase="true"
url="${basedir}\generated\${project.distname}.wsdl"
verbose="true"
debug="true">
</axis-wsdl2java>
<java
classname="org.apache.axis.utils.Admin"
fork="true"
failonerror="true"
classpathref="compile.classpath"
dir="${basedir}\WEB-INF\">
<arg value="server" />
<arg file="${basedir}\generated\com\zlatozar\soap\deploy.wsdd" />
</java>
<!-- copy the files we need to the src/com/zlatozar/soap dir -->
<copy todir="src/com/zlatozar/soap" includeEmptyDirs="no">
<fileset dir="generated/com/zlatozar/soap">
<patternset>
<include name="*SoapBindingSkeleton.java" />
</patternset>
</fileset>
</copy>
<!-- copy the wsdd to WEB-INF/server-config.wsdd -->
<copy file="generated/com/zlatozar/soap/deploy.wsdd"
tofile="WEB-INF/server-config.wsdd" />
<!-- copy generated WSDL file -->
<copy file="generated/${project.distname}.wsdl"
todir="." />
</target>
Такааа, да поясним. Създавам директория generated, която след това мога да изтрия лесно. Генерирам wsdl файла и го използвам за генерацията на SaysomethingSoapBindingSkeleton. В soap пакета трябва да имам генериран SaysomethingSoapBindingSkeleton.java, а в WEB-INF server-config.wsdd. В писането на build.xml обърнете внимание на namespace="urn:soap.zlatozar.com" - името на пакета ама в обратен ред и прехвърлянето на generated/com/zlatozar/soap/deploy.wsdd в WEB-INF/server-config.wsdd.
До тук бяхме с Axis. Да се заемем с конфигурирането на Hibernate3.
Само да кажа, че бях доста затруднен, не в самото конфигуриране а да измисля "доброто конфигуриране".
Тъй като това беше първия ми сблъсък с Hibernate3 исках да намерея бърз начин да науча основните стъпки.
От къде да прочета нещо?
Разберах как се "мапват" класове и таблици
По advanced неща
Как да генерирам java класовете от .hbm.xml файловете?
За целта трябва да имам hibernate-tools.jar. Този jar е част от Hibernate-tools проекта и за да го имате трябва да свалите този
проект, който не е никак малък като обем и от там да си гепите толкова полезния tool. При мен нещата стоят ето така:
<!-- Hibernate code generation -->
<target name="hibernategen" depends="init" description="Generate what hibernate needs">
<property name="libHibernatetool" value="lib/hibernatetool" />
<path id="hibernateTool">
<pathelement location="${outputDir}" />
<fileset dir="${libHibernatetool}">
<include name="*.jar" />
</fileset>
</path>
<taskdef name="hibernate-tool" classname="org.hibernate.tool.ant.HibernateToolTask" classpathref="hibernateTool" />
<hibernate-tool>
<configuration configurationfile="${conf}/hibernate.cfg.xml" />
<hbm2ddl export="true" drop="true" create="true" format="true" outputfilename="dbschema.sql" destdir="data" />
<hbm2java destdir="${sourceDir}" />
</hibernate-tool>
</target>
След като имам нужните .hbm.xml лесно мога да получа нужните ми класове и sql-a.
Една добра идея е да отделя генерираните неща от нещата, които се променят. Какво имам в предвид. При генерацията се получават POJO обекти и нищо повече, а аз може да искам да добавя нещо повече в тези обекти, като utility методи. Как да отделя променящото се от непроменящото се? Вижте:
<hibernate-mapping package="com.zlatozar.persistence.base"> <class name="SomethingBase" table="SAYSOMETHING_T"> <meta attribute="implement-equals">true</meta> <meta attribute="scope-field">protected</meta> <id name="sys_oid" column="SYS_OID" type="long"> <generator class="increment" /> </id> <property name="id" type="string" column="ID_C"> <meta attribute="use-in-equals">true</meta> </property> <property name="something" type="string" column="SOMETHING_C"> <meta attribute="use-in-equals">true</meta> </property> </class> </hibernate-mapping>
И така имам си com.zlatozar.persistens.base за POJO обектите - hibernate да си генира там каквото иска. Аз си имам:
public class Something extends SomthingBase
и си работя с него. А самото генериране не е сложно като видяхте. Почти свършихме само още няколко мисли за hibernate.
Остана управлението на сесиите към базата. Аз имам един Singleton клас InitSessionFactory, който отваря и затваря сесията.
public static SessionFactory getInstance() {
if ( sessionFactory == null )
initSessionFactory();
return sessionFactory;
}
a всички запитвания минават през QueryManager. Нещo такова:
public static void saveSomthing(SomethingBase aSome) {
Transaction tx = null;
Session session = InitSessionFactory.getInstance().getCurrentSession();
try {
tx = session.beginTransaction();
session.save(aSome);
tx.commit();
} catch (HibernateException e) {
log.error("Opperation will be rolled back", e);
if (tx != null && tx.isActive())
tx.rollback();
}
}
Е това е за сега. Решил съм да споделям и програмиските си главоблъскници. Meчтая си скоро да напиша същото и на Lisp...
.....
Какво ли ще вечерям все пак?

2 comments:
Mnogo gotin post!
Здрасти,
Няма да е трудно да се направи и на Лисп :)
Започни от тук: http://www.adampetersen.se/articles/lispweb.htm
Няма да е трудно и да се добавят Web Services. Очаквам и същата статия на Лисп. Успех.
Post a Comment