Sheila's Blog

Software Development

Sheila's Blog header image 4

Automated Build – Mysql database scripts

March 30th, 2009 by Sheila
Respond

Having set up the basic folder structure in Automated Build Databse scripts you now need to add the mysql scripts.

You create them in PROJECT_HOME/db/mysql/testdb db/mysql/testdb/build-mysql-testdb.xml db/mysql/testdb/createTables.sql db/mysql/testdb/dropTables.sql db/mysql/testdb/populateTables.sql db/mysql/testdb/testData.sql

The build script itself is fairly self-explanatory.  It replicates the targets from the master build script.  It reads the mysql.properties and schema.properties files and adds the correct jar file to the classpath.

For each target you first check if the property mysql.db.enable has been set so the target will only run if you’ve configured schema.properties to include the mysql database.   For each target you connect to the database and execute the sql in the appropriate sql script.

mysql/testdb/build-mysql-testdb.xml: <?xml version="1.0"?> <project basedir="." name="-build"> <property file="../../config/mysql.properties" /> <property file="../../config/schema.properties" /> <property name="lib.dir" value="../../lib" /> <!-- Class path settings --> <fileset id="lib" dir="${lib.dir}/mysql"> <include name="*.jar" /> </fileset> <path id="classpath"> <fileset refid="lib" /> </path> <target name="createUser" if="mysql.db.enable" description="Creates user and schema specified in properties file"> <echo>Executing mysql-airline:createUser </echo> <sql driver="${mysql.driver}" url="${mysql.url}" userid="${mysql.rootuser}" password="${mysql.rootpass}" print="${print}"> <classpath refid="classpath"/> create schema ${mysql.schema}; GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,ALTER,INDEX,DROP ON ${mysql.schema}.* TO '${mysql.username}'@'${mysql.user.location}' IDENTIFIED BY '${mysql.password}'; </sql> </target> <target name="dropUser" if="mysql.db.enable" description="Drops user and schema specified in properties file"> <echo>Executing mysql-airline:dropUser </echo> <sql driver="${mysql.driver}" url="${mysql.url}" userid="${mysql.rootuser}" password="${mysql.rootpass}" print="${print}" onerror="continue"> <classpath refid="classpath"/> drop schema ${mysql.schema}; drop user '${mysql.username}'@'${mysql.user.location}'; </sql> </target> <target name="recreateTables" if="mysql.db.enable" depends="dropUser,createUser,createTables,populateTables,testData" description="refreshes the database tables"/> <target name="createTables" if="mysql.db.enable" description="Creates Database Tables"> <echo>Executing mysql-airline:createTables </echo> <sql driver="${mysql.driver}" src="${mysql.create.tables.script}" url="${mysql.url}/${mysql.schema}" userid="${mysql.username}" password="${mysql.password}" print="${print}"> <classpath refid="classpath"/> </sql> </target> <target name="populateTables" if="mysql.db.enable" description="Populates Database Tables"> <echo>Executing mysql-airline:populateTables </echo> <sql driver="${mysql.driver}" src="${mysql.populate.tables.script}" url="${mysql.url}/${mysql.schema}" userid="${mysql.username}" password="${mysql.password}" print="${print}"> <classpath refid="classpath"/> </sql> </target> <target name="dropTables" if="mysql.db.enable" description="Drops Database Tables"> <echo>Executing mysql-airline:dropTables </echo> <sql driver="${mysql.driver}" url="${mysql.url}/${mysql.schema}" userid="${mysql.username}" password="${mysql.password}" src="${mysql.drop.tables.script}" print="${print}" onerror="continue"> <classpath refid="classpath"/> </sql> </target> <target name="testData" if="mysql.db.enable" description="Refreshes test data"> <echo>Executing mysql-airline:testData </echo> <sql driver="${mysql.driver}" src="${mysql.testdata.script}" url="${mysql.url}/${mysql.schema}" userid="${mysql.username}" password="${mysql.password}" print="${print}"> <classpath refid="classpath"/> </sql> </target> </project>

You then edit the sql scripts to add the tables and data needed for your schema.  Note that for targets that drop tables or users we set onerror=”continue” so that the build won’t fail if you add in new tables and attempt to drop them before they’ve been created.

Tags:   · No Comments.

Automated Build – OracleXe database scripts

March 27th, 2009 by Sheila
Respond

Having set up the database folder structure in Automated Build Database scripts you now need to add the oracle xe scripts.

You create them in PROJECT_HOME/db/oraclexe/testdb db/oraclexe/testdb/build-oraclexe-testdb.xml db/oraclexe/testdb/createTables.sql db/oraclexe/testdb/dropTables.sql db/oraclexe/testdb/populateTables.sql db/oraclexe/testdb/testData.sql

The build script itself is fairly self-explanatory.  It replicates the targets from the master build script.  It reads the xe.properties and schema.properties files and adds the correct jar file to the classpath.

For each target you first check if the property oraclexe.db.enable has been set so the target will only run if you’ve configured schema.properties to include the oraclexe database.   Then in each target you connect to the database and execute the statements in the appropriate sql script.

oraclexe/testdb/build-xe-testdb.xml: <?xml version="1.0"?> <project basedir="." name="xe-mdm-build"> <property file="../../config/schema.properties" /> <property file="../../config/xe.properties" /> <property name="template.dir" value="../templates" /> <property name="lib.dir" value="../../lib" /> <property name="build.dir" value="../../build" /> <!-- Class path settings --> <fileset id="lib" dir="${lib.dir}/oracle"> <include name="*.jar" /> </fileset> <path id="classpath"> <fileset refid="lib" /> </path> <target name="init" if="xe.db.enable"> <mkdir dir="${build.dir}"/> </target> <target name="clean" if="xe.db.enable"> <delete dir="${build.dir}"/> </target> <target name="gen-sql" if="xe.db.enable" depends="clean,init" description="Generates the sql files"> <copy file="${template.dir}/createUser.template" tofile="${build.dir}/createTestUser.sql" overwrite="true"> <!-- replace template tokens marked with ~~ before and after --> <filterset begintoken="~~" endtoken="~~"> <filter token="template.username" value="${xe.username}" /> <filter token="template.password" value="${xe.password}" /> </filterset> </copy> <copy file="${template.dir}/dropUser.template" tofile="${build.dir}/dropTestUser.sql" overwrite="true"> <!-- replace template tokens marked with ~~ before and after --> <filterset begintoken="~~" endtoken="~~"> <filter token="template.username" value="${xe.username}" /> </filterset> </copy> </target> <target name="createUser" if="xe.db.enable" depends="gen-sql" description="Creates user specified in properties file"> <echo>Executing xe-mdm:createUser </echo> <exec dir="${template.dir}" executable="cmd"> <arg line="/c run.bat ${xe.rootuser} ${xe.rootpass} ${xe.sid} ${build.dir}/createTestUser.sql"/> </exec> </target> <target name="dropUser" if="xe.db.enable" depends="gen-sql" description="Creates user specified in properties file"> <echo>Executing xe-mdm:dropUser </echo> <exec dir="${template.dir}" executable="cmd"> <arg line="/c run.bat ${xe.rootuser} ${xe.rootpass} ${xe.sid} ${build.dir}/dropTestUser.sql"/> </exec> </target> <target name="recreateTables" depends="dropTables,createTables,populateTables,testData" description="Refreshes the database tables"/> <target name="createTables" if="xe.db.enable" description="Creates Database Tables"> <echo>Executing xe-mdm:createtables </echo> <sql driver="${xe.driver}" src="${xe.create.tables.script}" url="${xe.url}" userid="${xe.username}" password="${xe.password}" print="${print}"> <classpath refid="classpath"/> </sql> </target> <target name="populateTables" if="xe.db.enable" description="Populates Database Tables"> <echo>Executing xe-mdm:populateTables </echo> <sql driver="${xe.driver}" src="${xe.populate.tables.script}" url="${xe.url}" userid="${xe.username}" password="${xe.password}" print="${print}"> <classpath refid="classpath"/> </sql> </target> <target name="dropTables" if="xe.db.enable" description="Drops the Database Tables"> <echo>Executing xe-mdm:dropTables </echo> <sql driver="${xe.driver}" src="${xe.drop.tables.script}" url="${xe.url}" userid="${xe.username}" password="${xe.password}" print="${print}" onerror="continue"> <classpath refid="classpath"/> </sql> </target> <target name="testData" if="xe.db.enable" description="Refreshes test data"> <echo>Executing xe-mdm:testData </echo> <sql driver="${xe.driver}" src="${xe.testdata.script}" url="${xe.url}" userid="${xe.username}" password="${xe.password}" print="${print}"> <classpath refid="classpath"/> </sql> </target> </project>

For oraclexe you need to generate scripts for creating and dropping the user and run them from a batch file.  The values in xe.properties are used to fill in the details.  Create the following templates:

oraclexe/templates/createUser.template: create user ~~template.username~~ identified by ~~template.password~~ DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp QUOTA 50M ON users; GRANT CONNECT, RESOURCE TO ~~template.username~~;

oraclexe/templates/dropUser.template:

In createUser.template we want to sustitute a value for the token template.username. We choose ~~ to indicate the start and end of the token. In the gen-sql target you state the start and end of the tokens are ~~ and indicate which value should replace the token in the template. In this case ~~template.username~~ will be replaced with the xe.username value. The generated sql scripts will be stored in db/build.

oraclexe/templates/run.bat:@echo off SET sysdba_userid=%1 SET sysdba_psswd=%2 SET sid=%3 SET sql_file=%4 SQLPlus %sysdba_userid%/%sysdba_psswd%@%sid% as sysdba @%sql_file% echo Finished processing, exiting ...

The createUser and dropUser targets are run after gen-sql. They execute a command line prompt and pass the database connection details and sql script to run.bat before launching it.

Now the only thing left to do is add the sql statements to the .sql scripts to create the database you want.  Note that for targets that drop tables or users we set onerror="continue" so that the build won't fail if you add in new tables and attempt to drop them before they've been created.

Tags:   · No Comments.

Automated Build – Database scripts

March 25th, 2009 by Sheila
Respond

When you’re developing a project, you put all your java code into source control.  You should also include database scripts in source control.  These scripts can be used by developers to keep their local copies of the database up to date in the same way as they get the latest code from source control.

As part of your automated build system you should run the database scripts whenever there’s a change to keep your code and database in synch.  You can add the code into your existing build in a db folder or check it into source control separately.

You follow almost the same process whether adding scripts for mysql or oraclexe and can modify the scripts to cater for multiple schemas across different databases.   In this example we’ll cover the configuration for setting up the testdb schema in both oraclexe and mysql before writing the scripts for each. This example assumes you already have both databases running locally on your machine.

To start with you create a PROJECT_HOME/db folder which contains: db/config db/datasources db/lib db/oraclexe/testdb db/mysql/testdb

In the lib folder add the connector jars for the databases eg. db/lib/oracle/ojdbc5.jar db/lib/mysql/mysql-connector-java-5.1.6-bin.jar

In the config folder we need some property files to configure the build. db/config/mysql.properties db/config/xe.properties db/config/schema.properties

Schema.properties is used to configure the build depending on whether you want to just use oracle xe or mysql or use both.  In this example oraclexe is enabled and mysql is disabled.

schema.properties: ## ## Use this property file to enable or disable the databases and ## schemas you want to run the build scripts against. ## Comment them out with a # if you don't want to enable a ## setting. #Databases xe.db.enable xe.username=testdb xe.password=testdb #mysql.db.enable #mysql.username=testdb #mysql.password=testdb

The next two give the access details for your oracle xe and mysql databases.

mysql.properties: # MySql Database Details mysql.rootuser=root mysql.rootpass=password mysql.url=jdbc:mysql://localhost:3306 mysql.schema=testdb mysql.driver=com.mysql.jdbc.Driver mysql.user.location=localhost mysql.create.tables.script=createTables.sql mysql.drop.tables.script=dropTables.sql mysql.populate.tables.script=populateTables.sql mysql.testdata.script=testData.sql print=false

xe.properties: # Oracle XE Database Details xe.rootuser=sys xe.host=localhost xe.sid=XE xe.rootpass=password xe.port=1521 xe.driver=oracle.jdbc.OracleDriver xe.url=jdbc:oracle:thin:@${xe.host}:${xe.port}:${xe.sid} xe.create.user.script=createUser.sql xe.delete.user.script=dropUser.sql xe.create.tables.script=createTables.sql xe.drop.tables.script=dropTables.sql xe.populate.tables.script=populateTables.sql xe.testdata.script=testdata.sql print=false

We start off with a master build.xml script in PROJECT_HOME which calls oraclexe/testdb/build-xe-testdb.xml and mysql/testdb/build-mysql-testdb.xml script.  The ‘ant dir’ value specifies the directory the script to be called in, ‘antfile’ gives the name of the script, and lastly you specify the target to be run in that script.

db/build.xml: <?xml version="1.0"?> <project basedir="." name="db-master-build" default="recreateTables"> <property name="build.dir" value="${basedir}/build" /> <target name="clean"> <delete dir="${build.dir}" failonerror="false" /> </target> <target name="install" depends="createUsers,createTables,populateTables,testData" description="Creates all users and schemas for a fresh install" /> <target name="recreateTables" description="Refreshes the database tables for all schemas"> <ant dir="oraclexe/testdb" antfile="build-xe-testdb.xml" target="recreateTables" /> <ant dir="mysql/testdb" antfile="build-mysql-testdb.xml" target="recreateTables" /> </target> <target name="createUsers" description="Creates users for all schemas"> <ant dir="oraclexe/testdb" antfile="build-xe-testdb.xml" target="createUser" /> <ant dir="mysql/testdb" antfile="build-mysql-testdb.xml" target="createUser" /> </target> <target name="dropUsers" description="Drops users for all schemas"> <ant dir="oraclexe/testdb" antfile="build-xe-testdb.xml" target="dropUser" /> <ant dir="mysql/testdb" antfile="build-mysql-testdb.xml" target="dropUser" /> </target> <target name="createTables" description="Creates Database Tables for all schemas"> <ant dir="oraclexe/testdb" antfile="build-xe-testdb.xml" target="createTables" /> <ant dir="mysql/testdb" antfile="build-mysql-testdb.xml" target="createTables" /> </target> <target name="populateTables" description="Populates Database Tables for all schemas"> <ant dir="oraclexe/testdb" antfile="build-xe-testdb.xml" target="populateTables" /> <ant dir="mysql/testdb" antfile="build-mysql-testdb.xml" target="populateTables" /> </target> <target name="dropTables" description="Drops Database Tables for all schemas"> <ant dir="oraclexe/testdb" antfile="build-xe-testdb.xml" target="dropTables" /> <ant dir="mysql/testdb" antfile="build-mysql-testdb.xml" target="dropTables" /> </target> <target name="testData" description="Refreshes test data for all schemas"> <ant dir="oraclexe/testdb" antfile="build-xe-testdb.xml" target="testData" /> <ant dir="mysql/testdb" antfile="build-mysql-testdb.xml" target="testData" /> </target> </project>

The install target is used to create the database schemas from scratch – including the users.

The recreateTables target is the default and is used when the schemas and users already exist and you just want to drop and create the tables to pick up changes.

The rest of the targets can be called separately for creating tables, populating them with default data (eg. lists of countries that would always be needed for an application) adding some test data and deleting the tables again.

The next step is to add the oraclexe-specific scripts and the mysql-specific scripts.

Tags:   · No Comments.

Automating your Build – The Very Basics for a Beginner

March 24th, 2009 by Sheila
Respond

This post covers the absolute basics for a beginner of setting up a minimal build script for building and deploying code as a jar/war/ear file. The aim is to show a simple build structure and script rather than building a proper application. The scripts are mostly self-explanatory. The end result can be used as a starting point for later posts covering more difficult topics including building sample applications, testing and automating your build with CruiseControl.

Prerequisite:
You need java and ant installed on your machine and running from a command prompt.

The first thing you need when setting up a new project is to decide on the structure. By using standard conventions about how you structure your code it makes it easier for others to work on it. You’ll see a similar type of build structure used in tools like Seam’s seamgen except more complicated than this one.

The root folder of your project will be referred to as PROJECT_HOME in these notes. The first thing to do is create the following folders in PROJECT_HOME:
config – holds configuration files
lib – holds any jar files
resources – resource and config files to be copied into a jar, war or ear file during the build
src – for source java files
web – for web applications pages, css, images etc

Now add an ant build script file PROJECT_HOME/build.xml which is used to build the project. Also add PROJECT_HOME/config/build.properties.sample. This file is added to source control. Each user that checks out the project will copy this file and rename it to build.properties which will be used by the build script. Any subsequent changes to the property file in source control will not overwrite a local user’s settings and this also forces a user to set the properties correctly when checking out a new project.

PROJECT_HOME/config/build.properties.sample: project.name=example web.pages.dir=web javac.debug=true javac.deprecation=false jboss.home = C:/Applications/jboss-4.2.2.GA/

We start with a basic build script that reads the build.properties file, adds some other properties, a class path and the targets init, compile and clean. We’ll add to this as we go along.

The build process generates the folder structure for a jar file, war file and ear file in exploded-archives. It will then package those into actual archive files in the dist directory. This allows you to check what’s being added without having to examine the final archive files. You also configure a directory to deploy in this case an ear file to a web application server.

PROJECT_HOME/build.xml <project name="example" default="compile" basedir="."> <!-- set properties for this build --> <property name="config.dir" value="config" /> <property file="${config.dir}/build.properties" /> <property name="src.dir" value="src" /> <property name="lib.dir" value="lib" /> <property name="resources.dir" value="resources" /> <property name="dist.dir" value="dist" /> <property name="archive.dir" value="exploded-archives" /> <property name="ear.dir" value="${archive.dir}/${project.name}.ear" /> <property name="jar.dir" value="${archive.dir}/${project.name}.jar" /> <property name="war.dir" value="${archive.dir}/${project.name}.war" /> <property name="deploy.dir" value="${jboss.home}/server/default/deploy" /> <!-- Class path settings --> <fileset id="lib" dir="${lib.dir}"> <include name="*.jar" /> </fileset> <path id="build.classpath"> <fileset refid="lib" /> </path> <target name="init" description="Initialize the build"> <echo>Initialising build...</echo> <mkdir dir="${jar.dir}" /> <mkdir dir="${ear.dir}" /> <mkdir dir="${war.dir}" /> <mkdir dir="${archive.dir}" /> <mkdir dir="${dist.dir}" /> </target> <target name="compile" depends="init" description="Compile the Java source code"> <echo>Compiling java source code...</echo> <javac classpathref="build.classpath" destdir="${jar.dir}" debug="${javac.debug}" deprecation="${javac.deprecation}" nowarn="on"> <src path="${src.dir}" /> </javac> </target> <target name="clean" description="Cleans up the build directorys"> <echo>Cleaning build directories...</echo> <delete dir="${ear.dir}" /> <delete dir="${war.dir}" /> <delete dir="${jar.dir}" /> <delete dir="${archive.dir}" /> <delete dir="${dist.dir}" /> </target> </project>

The build.classpath is set to simply include any jar files in the lib folder.

The init target creates any directories that are needed by the build.

The clean target deletes any directories that were created by the build.

The compile target looks in the src folder for any .java files and compiles them into the exploded-archives/example.jar folder.

To test the build script add the file

PROJECT_HOME/src/com/example/Parent.java package com.example; public class Parent { private Long parentId; public Long getParentId() { return this.parentId; } public void setParentId(Long parentId) { this.parentId = parentId; } }

Open a command prompt in the PROJECT_HOME folder and run the command ‘ant’. By default (default=”compile”) it will run the compile target. The compile target states that it depends on the init target, so the init target will be run first to create the folders that are needed. When the script finishes running you will find that exploded-archives/example.jar/com/example contains the compiled Parent.class.

We want to build and deploy a web application so we add the target gen-war-files which will be used to copy anything that’s needed into the exploded war file before it gets packaged. We’ll start with a target like this: <?xml version="1.0"?> <target name="gen-war-files" depends="compile" description="Generates the war files"> <echo>Copying war files...</echo> <copy todir="${war.dir}"> <fileset dir="${basedir}/${web.pages.dir}/pages" /> </copy> <copy todir="${war.dir}"> <fileset dir="${basedir}/${web.pages.dir}"> <include name="images/**/*" /> <include name="css/**/*" /> </fileset> </copy> <copy todir="${war.dir}/WEB-INF/lib"> <fileset dir="${lib.dir}"> <includesfile name="${config.dir}/war-jars.list" /> </fileset> </copy> <copy todir="${war.dir}/WEB-INF"> <fileset dir="${resources.dir}/WEB-INF"> <include name="*.*"/> </fileset> </copy> </target>

A war file contains a WEB-INF folder where compiled classes and required jar files are put. Also files such as web.xml which gives configuration and deployment for the web components. So create resources/WEB-INF.

PROJECT_HOME/resources/WEB-INF/web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> </web-app>

The build script copies any files in resources/WEB-INF to the war folder and also copies any jar files that are needed to the WEB-INF/lib folder. You can configure which of the jar files in PROJECT_HOME/lib get copied over in PROJECT_HOME/config/war-jars.list. For now just create an empty file of that name in the config folder.

This target copies any files in web/pages to exploded-archives/example.war and will also copy over the images and css folders. We don’t have any files to copy at the moment, but will add an index page to see that it does get added.

web/pages/index.jsp <?xml version="1.0" encoding="ISO-8859-1" ?> <html> <head> <title>The Index Page</title> </head> <body> Index page </body> </html>

Try the command ‘ant gen-war-files’ and you will see that exploded-archives/example.war now contains the index page.

We now add the gen-ear-files and archive targets. Similarly to the war file, the ear file requires a META-INF folder containing information about the ear file. So you can add resources/META-INF for holding these files and an empty config/ear-jars.list to configure which jar files are needed in the ear file. We will add an application.xml which is the deployment descriptor for ear files.

resources/META-INF/application.xml <?xml version="1.0" encoding="UTF-8"?> <application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" version="5"> <display-name>example</display-name> <module> <ejb>example.jar</ejb> </module> <module> <web> <web-uri>example.war</web-uri> <context-root>/example</context-root> </web> </module> </application>

The gen-ear-files target will take care of copying this to the correct location in the ear file then the archive target will take all the output in exploded-archives and package up all the files into the dist directory. Test this using ‘ant archive’ <target name="gen-ear-files" depends="gen-war-files" description="Generates the ear files"> <echo>Copying the ear files...</echo> <mkdir dir="${ear.dir}/lib" /> <copy todir="${ear.dir}/lib"> <fileset dir="${lib.dir}"> <includesfile name="${config.dir}/ear-jars.list" /> </fileset> </copy> <copy todir="${ear.dir}/META-INF"> <fileset dir="${resources.dir}/META-INF"> <include name="*.*"/> </fileset> </copy> </target> <target name="archive" depends="gen-war-files,gen-ear-files" description="Packages all the archives"> <echo>Packaging all archives...</echo> <jar jarfile="${dist.dir}/${project.name}.jar" basedir="${jar.dir}" /> <jar jarfile="${dist.dir}/${project.name}.war" basedir="${war.dir}" /> <jar jarfile="${dist.dir}/${project.name}.ear"> <fileset dir="${ear.dir}"/> <fileset dir="${dist.dir}"> <include name="${project.name}.jar"/> <include name="${project.name}.war"/> </fileset> </jar> </target>

We now want to deploy the ear file that has been created to a web application server. In our example we’re using JBoss. You set the jboss.home directory in build.properties already. Now add some more build targets <target name="clean-deploy" depends="clean, deploy" description="A full clean deploy"/> <target name="deploy" depends="archive" description="Deploy to a JBoss web server"> <echo>Deploying to JBOSS web server...</echo> <fail unless="jboss.home">jboss.home not set</fail> <copy todir="${deploy.dir}" file="${dist.dir}/${project.name}.ear" /> </target> <target name="undeploy" description="Undeploy from a JBoss web server"> <echo>Undeploying from JBOSS web server...</echo> <delete file="${deploy.dir}/${project.name}.ear" /> </target>

Now run the clean-deploy target (set this to default) and try accessing http://localhost:8080/example/
You should see the index page display if everything is working correctly. You now have an extremely basic build script running.  You should go through each line and check that you understand what it does – you can get more detailed information in the Ant User Manual.

Tags:   · No Comments.

Alfresco Part Four – Accessing Alfresco webclient from a url

March 20th, 2009 by Sheila
Respond

Having created a space in Part Three, we now want to access it from our web application. The Alfresco web-client provides some servlets to allow URL-based access to pages within the client. To use these servlets without having to log in you have to request an authentication ticket via a web services call and append it to the url.

We want to access the node 066a443a-a43f-4911-9ef2-56cd96a3392e in this example which is the reference for the space we created. The url is

http://localhost:8080/alfresco/n/browse/workspace/SpacesStore/066a443a-a43f-4911-9ef2-56cd96a3392e

We will be using the browse outcome of the ExternalAccessServlet which takes a node reference of the folder to display.

First we get an authentication ticket: String userName = "admin"; String password = "admin"; String ticket = ""; // Start the session and get a ticket try { AuthenticationServiceSoapBindingStub authenticationService = (AuthenticationServiceSoapBindingStub) new AuthenticationServiceLocator().getAuthenticationService(); AuthenticationResult result = authenticationService.startSession(userName, password); ticket = result.getTicket(); } catch (ServiceException serviceEx) { System.out.println("javax.xml.rpc.ServiceException: " + serviceEx); } catch (RemoteException remoteEx) { System.out.println("java.rmi.RemoteException: " + remoteEx); } System.out.println("Ticket is: " + ticket);

Now we append the ticket to the url: String BROWSE_SPACE = "http://localhost:8080/alfresco/n/browse/workspace/SpacesStore/"; String nodeReference = "066a443a-a43f-4911-9ef2-56cd96a3392e"; String APPEND_TICKET = "?ticket="; String url = BROWSE_SPACE + nodeReference + APPEND_TICKET + ticket;

You can then access the workspace page directly by opening up a url similar to this:

http://localhost:8080/alfresco/n/browse/workspace/SpacesStore/066a443a-a43f-4911-9ef2-56cd96a3392e?ticket=TICKET_c02f49a5cb8db3f59e4b538d25deaa202f4de493

You can get more information about accessing the web client using urls here.

Tags:   6 Comments

Alfresco Part Three – Create a new workspace using a web service call

March 19th, 2009 by Sheila
Respond

In Part One we covered the default installation of Alfresco and in Part Two we covered a JBoss installation. Now we’re going to look at how you would handle linking up your own web application with Alfresco web client.

The structure of the Alfresco repository is similar to that of nested folders or nodes. You start with a root folder and then add sub folders that can contain content or more folders. In Alfresco these folders are called spaces. Each space or piece of content has its own node reference.

In our scenario you have a standalone web application and want to be able to link to Alfresco’s Web Client from it. Alfresco will then handle all the content management functionality. You have objects in your web application for which you want a corresponding workspace in Alfresco to contain any content for that object. The functionality you need to add to your web application is:

1. When an object is created, create a corresponding workspace for that object in Alfresoc and retrieve the node reference for that workspace.
2. Store the objects and matching node references (you will need to do this yourself).
3. Select an object in your web application, get the node reference for the workspace in Alfresco, and open a new tab/window to view/update that workspace in Alfresco.
4. You also want to bypass the Alfresco login page / authentication in all these scenarios so the user doesn’t have to enter login details.

At a minimum you will need the following jars in your web application:

alfresco-web-service-client.jar
axis-1.4.jar
commons-discovery-0.2.jar
wsdl4j-1.6.2.jar
(jaxrpc.jar should already be available in a jboss application)
commons-logging-1.1.jar
wss4j.jar
opensaml-1.0.1.jar
xmlsec-1.4.1.jar

Depending on the jar files already included in your application you may need to add some more jars.

We’ll start with authenticating a web service call that will create a new space. To use a web service api you need to authenticate your session.

Authentication for web service API:

// Start the session
AuthenticationUtils.startSession(“admin”, “admin”);

try {
// Make your web service call in here
}
catch (Throwable e) {
System.out.println(e.toString());
e.printStackTrace();
}
finally {
// End the session
AuthenticationUtils.endSession();
}

To create a workspace called “child” in the “parent” workspace using a web service call:

// The default workspace store is SpacesStore
Store storeRefence = new Store(Constants.WORKSPACE_STORE, “SpacesStore”);

// We want to create a new folder or workspace called child in the parent folder under company home
// Create parent reference to the parent space in company home
ParentReference parentReference = new ParentReference(storeReference, null, “/app:company_home/cm:parent”,
Constants.ASSOC_CONTAINS, Constants.createQNameString(Constants.NAMESPACE_CONTENT_MODEL, “sample_folder”));

// Create the child folder
NamedValue[] properties = new NamedValue[] { Utils.createNamedValue(Constants.PROP_NAME, “Child Folder”) };
CMLCreate create = new CMLCreate(“1″, parentReference, null, null, null, Constants.TYPE_FOLDER, properties);
CML cml = new CML();
cml.setCreate(new CMLCreate[] { create });
UpdateResult[] results = WebServiceFactory.getRepositoryService().update(cml);

// From the result of the web service call we want to retrieve the node reference for the child folder so we can access it later
String childReference = results[0].getDestination().getUuid();
System.out.println(“Created a new workspace, node is: ” + childReference);

In Part Four we will handle generating a url to access the space we have just created and bypassing the login page when accessing the url.

Tags:   · 1 Comment

Rebounding

March 19th, 2009 by Sheila
Respond

I’ve been trying out different forms of exercise each year to see which ones I like best, and also just to vary the workouts I do so that they’re more effective.  For about 4 months now I’ve been taking rebounding classes at least once a week.  Luckily for me, they’re one of the free classes offered by my local gym so they’re a great alternative to doing my own workout.

For a rebounding class each participant has their own ‘trampette’ – a mini trampoline that they use.  After checking all the legs to make sure they’re tight and stable, you start off with a warmup.  The general idea is that you do a lot of different jumps and movements as you bounce on the trampoline.  While it looks like fun, it’ s actually a tough workout aswell and really gets your heart pumping.

There are many medical benefits listed for doing rebounding.  It exercises parts of the body you wouldn’t usually hear much about like the glandular and lymphatic systems.  It’s great for your heart and is very kind to your knees compared to pounding the treadmill.  Apparently it’s highly recommended by NASA.

What I really like about the instructor-led classes I do, is that there’s a wide variety of exercise that can be incorporated.  Aside from the cardio exercising – weights, balance and ab work can be included.  The trampette can be used as a bench for doing typical reps of weights.  It can also be used as a base for doing ab work.  And finally you can improve your balance by hopping on one leg, or standing on one as you lean forward and stretch the other out straight behind you.

It’s possibly to buy your own trampette and an exercise DVD to do at home.  I haven’t tried this yet as the classes are free to go to, but if I ever find myself without a gym membership again I’d definitely look into rebounding DVDs to try out at home.

Tags:   No Comments.

Alfresco Part Two – Deploying Alfresco on JBoss

March 18th, 2009 by Sheila
Respond

First we need to get the war version of alfresco from a download site.  The one I used was alfresco-labs-war-3Stable.zip. Extract the contents to a temporary folder.

The next step is to set up the database.  If you’ve already installed the default version of Alfresco you’ll need to drop the database and recreate it.  Either way, you’ll find mysql scripts in extra/databases/mysql for setting up or dropping the schema.   See Using Alfresco for document management Part One for details on how to do a default install of Alfresco and for configuring Open Office.

You now need to extract alfresco.war to edit the contents. I expanded it into exploded/alfresco.war so I can copy the alfresco.war folder directly into my jboss application when I’m ready to deploy.

We need to configure the war for deployment in JBoss. You’ll find the config files needed in the extensions/extension folder. The ones we want are:
custom-hibernate-dialect.properties
custom-repository.properties
custom-repository-context.xml

Copy these to exploded/alfresco.war/WEB-INF/classes/alfresco/extension

In custom-repository.properties you need to set the dir.root to point to where you want to store your alfresco data. Also uncomment and set the database values – don’t forget to disable the derby settings also.

Finally you need to set the external executable locations. What I did here was copy the ImageMagick and bin folders from the default Alfresco install into my root folder containing alf_data.  Otherwise you will need to install missing executables yourself.

custom-repository.properties: dir.root=C:/Alfresco/alf_data db.username=alfresco db.password=alfresco db.pool.initial=10 db.pool.max=100 ooo.exe=C:/Program Files/OpenOffice.org 3/program/soffice ooo.user=${dir.root}/oouser img.root=C:/Alfresco/ImageMagick swf.exe=C:/Alfresco/bin/pdf2swf db.driver=org.gjt.mm.mysql.Driver db.url=jdbc:mysql://localhost/alfresco

Now update the hibernate dialect in custom-hibernate-dialect.properties

I used JBoss 4.2.2 as my application server.  Before deploying you should edit jboss-4.2.2.GA/server/default/deploy/ejb3.deployer/META-INF/persistence.properties to change hibernate.bytecode.provider from javassist to cglib.

To avoid getting permgen out of memory errors you will probably need to configure your JBOSS_HOME/bin/run.bat to increase the memory size.

You can now try coping alfresco.war into your jboss application. Assuming this doesn't cause a spectacular display of stack traces, when the server finally starts up you can try accessing the application at http://localhost:8080/alfresco.

You're now ready to start figuring out how to use alfresco as it is, or integrate it with other applications.

Next - Creating a workspace using a web service API

Tags:   · 7 Comments

Alfresco Part One – Using Alfresco for document management

March 16th, 2009 by Sheila
Respond

Alfresco is a content management system that comes in two flavours – the open source Alfresco Labs and Alfresco Enterprise Edition.  I only looked at the open source version.  There’s quite a lot to look at, and unfortunately (as is too often the case) the documentation leaves a lot to be desired.  I was soon lost in a sea of versions of the applications and various ways of accessing content.  I was still none too clear how, or if it could be linked in with our existing application without significant work.

The first step was to install Alfresco… but which version?  The easiest thing to do is install the fully featured Alfresco Labs 3 and run that initially to investigate what Alfresco can do.  Eventually I would need to deploy it in JBoss, but first things first… This also gives you the option of cheating later and using some of the programs installed by default when they’re missing in a more customised deployment.

The installation:
Prior to installation I already had a local mysql database installed.  I downloaded Alfresco Labs 3 and ran the setup file.  I chose a custom install.  I deselected the jdk and open office and installed the remainder into C:/Alfresco.  I set my data to be stored in C:/Alfresco/alf_data and part of the installation included setting up the alfresco database using the mysql details I provided.


The configuration:

On the default install, extension files are stored in C:/Alfresco/tomcat/shared/classes/alfresco/extension.   Most of the settings will have been set correctly in the default install.

To configure my open office installation I edited custom-repository.properties which overrides the default values.
You start the server by accessing Start -> Programs -> Alfresco Labs -> Start Alfresco Server.  Unfortunately if there are any problems at all the window closes leaving you to search the logs or run the batch scripts manually to see what's going on.


Exploring the application:

Assuming your server starts up successfully, open up the Alfresco Explorer in the same menu location.  You can log in here as admin/admin.  This is the web client program which you can use to create users, assign spaces and edit content.  There's also Alfresco Share as an alternative which is more geared towards setting up collaborative sites.  It takes a bit of time to get familiar with the interface, but there's nothing too difficult to figure out.

You can explore the database schema.  The main tables to note are:
alf_authority which lists the user details
alf_node which gives you the node ids for the documents
avm_modes gives more details about the document nodes

The real fun and games start when you try to deploy a version other than the default install or access the repository or web client remotely.  Details on those to follow later...

Next - Deploying Alfresco Web Client in JBoss

Tags:   · No Comments.

Beginner's introduction to Seam

March 13th, 2009 by Sheila
Respond

Seam is one of the newer frameworks that has been gaining in popularity recently.  It was created by Gavin King who will already be familiar to Hibernate users.  It’s based on POJOs (Plain Old Java Objects) known as components and annotations for those components that reduce the need for extensive xml configuration files.  The components are wired together using dependency injection where components declare their dependencies, and Seam looks after getting the the components needed and injecting them for you.

While it’s still nowhere near as widespread as Spring, it brings some functionality that was missing in Spring, and can complement or replace Spring depending on what features you value in your web application.  From first experiences using the framework I can see many benefits to Seam, but would caution that it’s not quite so easy to use as some of the documentation might lead you to believe.  It can solve the problems it was designed to fix, but you have to be careful that you not only understand the problems, but also the solutions being applied.  You really need to code your application in a way that allows it to make use of what Seam is able to do well, otherwise you may inadvertantly bypass what you wanted.  If you don’t really understand the nuances of how Seam is solving typical problems, chances are your code isn’t going to be able to take advantage of what Seam does offer.  The other disadvantage is that in being able to use the framework in many different ways, a beginner can have trouble figuring out which way is best to implement something for their own particular scenario.  If you have a lot of very experienced developers on your team, these won’t be such big issues.  But it’s something to be aware of if you don’t already have Seam experience on the team.

So what problems is Seam supposed to solve?
One of Seam’s purposes is to fill in the gap between using JSF for the web tier which is usually based on stateless simple requests and responses, and EJB3 for the business tier which can deal with transactional resources.  Ordinarily to use these two frameworks together you need to write extra configuration code and backing beans.  Seam annotations allow business components to be used directly by the front end.  You use Entity objects to represent data that is mapped to a relational database table.  JSF web pages can display and capture data using the JSF Expression Language (EL) and map the data directly back to the backend.  You then have handlers that can update the data or perform business logic. There are also some extra seam tags available to use that fix some issues with JSF tags.

Another thing Seam brings to the table is more than just a Session scope or context.  The most interesting one being the Conversation scope.  The extra scopes mean that less data is stored in the session and can be managed in a wiser manner. More on the conversation scope another time as it’s a big area to get your head around.

The next solution Seam offers is to help end the LazyInitialization exceptions seen when using Hibernate (which was designed for use in Stateful applications) or other ORM (Object Relational Mapping) solutions with a Stateless framework.  These LazyInitialization exceptions occur because Hibernate is based on using objects containing data that can have other associated objects.  Lazy loading is used to avoid fetching large amounts of data that might not be used when loading a parent object that has other child objects associated with it.   The associated child objects are not loaded unless specifically requested.  But we run into a problem in a Stateless framework as the persistence context is destroyed once the parent is passed to the presentation layer.  If the presentation layer requests a child object, then a lazy loading exception is thrown as the persistence context is no longer available for the second request.  This means that DTOs (Data Transfer Objects) have to be used. Seam can keep the persistence context open until a page is fully rendered, or longer across a number of pages or a session.  So there should be no lazy loading exceptions and the performance improves when changes are only flushed to the database at the end of a conversation.

These are some of the main reasons why you might choose to use Seam in a project.  There’s a lot to cover in the Seam framework however, so this only scratches the surface before going into more detail in each area.

Tags:   No Comments.