Aug 28, 2009

Print Images Across Multiple Pages in Ubuntu

Ubuntu, which I use as my primary desktop OS is sweet, but it always has these little issues, when things you're used to in Windows are either hard or impossible to achieve. Well, there's nothing impossible when it comes to Linux, it just depends whether it'd take two minutes or five years to achieve whatever it is you want.

Today I had to print a large image across multiple pages and couldn't find an easy way to do this. Apparently, this is a known issue.

One messy way to do this (assuming you have the time and resources to get the right size) is this:

$ lp -d hp -o scaling=200 -o media=a4 filename.png


Where hp is the name of the printer and 200 is the size of the original image in percents.

Sadly enough, this only works for bitmap images. I had to export my original .svg file to .png, otherwise I was getting just a part of my svg which was cut to fit the A4 sheet.

Aug 20, 2009

Page Title in Wicket

For a simple Wicket webapp, setting page title can be as easy as using the wicket:message tag, like this:


<!DOCTYPE html>
<html>
<head>
<title><wicket:message key="page.title"></wicket:message></title>
</head>
...
</html>


Where in the .property file, related to each page, the page.title property can be set to whatever the page is titled.

When you have a complex application, however, you might need to have more control over the page title, specifically when the title has to be dynamically generated depending on, e.g., product name.

There might be different approaches to this, but the one I'm using allows to have a default title for all pages which can be overriden for each particular page.

Most of the times, there's a base page for all the other pages. The pageTitle label is added to the page, HTML placeholder for which is the <title> tag.

BasePage.html

<!DOCTYPE html>
<html>
<head>
<title wicket:id="pageTitle"></title>
</head>
...
</html>


Then we add the component to the page.
The idea is to have a quick method to replace the pageTitle component model once the data required to construct title is loaded. So the setPageTitle(IModel model) method is added.

BasePage.java

public class BasePage {

public BasePage() {
add(new Label("pageTitle", new StringResourceModel("page.title", this, null)));
}

protected void setPageTitle(IModel model) {
get("pageTitle").setModel(model);
}
}


Now, in the ProductPage that extends the base page, we fetch the product data and update the page title:

ProductPage.java


public class ProductPage {

final int id;

public ProductPage(PageParameters params) {
id = params.getInt("id");
final Product p = productDao.get(id);
...
// add components or whatever
...
setPageTitle(new StringResourceModel("pageTitle", this, new Model(p)));
}
}


The ProductPage.properties has also to be updated, assuming the product has getTitle() and getPrice() methods:

ProductPage.properties

page.title=Shiny ${title} for just ${price}!


That's the basic idea. I'm not particularly happy with this approach as it requires to always remember to update the pageTitle component and push the title into the page. I'd actually prefer a way to override something so the title would be pulled for me. Ideally, that would look like this:


add(new Label("pageTitle", getPageTitle()));


Unfortunately, in this case if the label is added on the BasePage, even if the getPageTitle() is overriden in the ProductPage, since BasePage is constructed prior to any logic found in ProductPage, there is no data to return in getPageTitle().

May 29, 2009

Switching to Sun JDK/JRE/JVM in Ubuntu

Turns out, I've been using OpenJDK all this time. Apparently, this is not so bad, but when I tried to play with Ant from command line, I got BUILD FAILED kind of errors.

The symptoms are:

$ ant -version
Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk/lib/tools.jar
Apache Ant version 1.7.1 compiled on November 10 2008

and

$ java -version
java version "1.6.0_0"
OpenJDK Runtime Environment (IcedTea6 1.4.1) (6b14-1.4.1-0ubuntu10)
OpenJDK Server VM (build 14.0-b08, mixed mode)


The easiest way to solve this for me was to force Sun's “original” JDK.

Apparently, once you've installed sun-java6-bin, -jre and -jdk packages, you have to
$ sudo update-java-alternatives -s java-6-sun

and then edit /etc/jvm so that it looks something like this, with /usr/lib/jvm/java-6-sun on top:

/usr/lib/jvm/java-6-sun
/usr/lib/jvm/java-gcj
/usr/lib/jvm/ia32-java-1.5.0-sun
/usr/lib/jvm/java-1.5.0-sun
/usr

May 24, 2009

Configure Tomcat To Serve Static Files

Tomcat is often considered to be too slow to serve static content. That is why on production servers, static files are often served by a separate web server like Apache Server or lighthttpd.

In development, however, you sometimes might need a quick-and-dirty solution for serving large amounts of static files stored somewhere on hard disk. One example of such usage would be an service that stores and organizes image files.

Suppose you have a /var/project/images directory which stores a number of images for your project. If you want this directory to be exposed through http protocol, all you have to do is to add a <Context> parameter to the <Host> section of Tomcat's server.xml:

<Server port="8025" shutdown="SHUTDOWN">
...
<Service name="Catalina">
...
<Engine defaultHost="localhost" name="Catalina">
...
<Host appBase="webapps"
autoDeploy="false" name="localhost" unpackWARs="true"
xmlNamespaceAware="false" xmlValidation="false">
...
<Context
docBase="/var/project/images"
path="/project/images" />
</Host>
</Engine>
</Service>
</Server>

Note that docBase parameter is set to the path on hard drive and path parameter is set to the context path for your files. Now, if you have a /var/project/images/sample.png file, it can be seen at http://localhost:8084/project/images/sample.png. Host name and port number may be different on your system, the ones listed here are default for the version of Tomcat bundled with NetBeans.

May 20, 2009

Add Syndication to symfony-driven Blog

If you have a standalone blog powered by symfony framework, and would like to add syndication with major blog services like Blogger or LiveJournal, you could use a PEAR “Services_Blogging” package.

Download



First, you download it and then install following this tutorial.

Extract the archive and put the contents in your symphony's lib/vendor/serviceblogging directory. You will also have to add the XML-RPC PEAR package. Download it, extract, then put XML_RPC_x.x.x folder in the servicesblogging directory (there must be a Services folder there) and rename from “XML_RPC_x.x.x” to “XML”

Install



Now, install the package as a symfony plugin. For this, edit the config/ProjectConfiguration.php:

class ProjectConfiguration extends sfProjectConfiguration
{

static protected $sbLoaded = false;

static public function registerServicesBlogging() {
if (self::$sbLoaded) {
return;
}

set_include_path(sfConfig::get('sf_lib_dir').'/vendor/servicesblogging'
.PATH_SEPARATOR.get_include_path());
require_once sfConfig::get('sf_lib_dir').'/vendor/servicesblogging/Services/Blogging.php';
self::$sbLoaded = true;
}

public function setup()
{
}
}


Enjoy



Now in your php code you can do the following to add a new post:

$bl = Services_Blogging::factory(
"LiveJournal",
"username", "password",
"http://livejournal.com",
"/interface/xmlrpc");

$post = $bl->createNewPost();
$post->title = $title;
$post->content = $text;
$bl->savePost($post);


Disable Auto-formatting



A little something for livejournal users: you can disable auto-formatting if you pass a special parameter to livejournal XML-RPC service. To do this the dirty, way, edit Services/Blogging/Driver/LiveJournal.php file of the Services_Blogging package.

Specifically, in the the Driver.savePost() method, add props parameter to the RPC call value:

...
$value = new XML_RPC_Value(
array(
'username' => $this->userdata['rpc_user'],
'auth_method' => new XML_RPC_Value('challenge', 'string'),
'auth_challenge' => new XML_RPC_Value(
$authdata['challenge'], 'string'
),
'auth_response' => new XML_RPC_Value(
$authdata['response'] , 'string'
),

'subject' => new XML_RPC_Value(
$post->{Services_Blogging_Post::TITLE}
),
'event' => new XML_RPC_Value(
$post->{Services_Blogging_Post::CONTENT}
),
'lineendings' => new XML_RPC_Value('pc'),

'year' => new XML_RPC_Value(date('Y', $time), 'int'),
'mon' => new XML_RPC_Value(date('n', $time), 'int'),
'day' => new XML_RPC_Value(date('j', $time), 'int'),
'hour' => new XML_RPC_Value(date('G', $time), 'int'),
'min' => new XML_RPC_Value(date('i', $time), 'int'),

'props' => new XML_RPC_Value(
array('opt_preformatted' => new XML_RPC_VALUE(true, 'boolean')),
'struct'),
),
'struct'
);

...


That's it.

Mar 24, 2009

Spring: Distributed Transactions across JCR and MySQL Database

Before following the steps described next, please be aware that this isn't an example of something you should use in production, but merely a sketch of how to wire Jackrabbit repository and a MySQL database to participate in distributed transactions. I haven't worked with XA before, nor I have a lot of experience with Spring, and it took me some time to get this working (mostly because of my ignorance in this area), so certain things are simplified, certain libraries are outdated, and sometimes I am not entirely sure what exactly I am doing. Hopefully, in future I will improve and update this example.

If you're new to Spring and transactions, you might want to read Java Transaction Design Strategies, a free book from InfoQ, and Chapter 9 of the Spring Reference.

To have transactions distributed across multiple datasources in Spring, you need an external transaction manager. By now, I am aware of three distributions that provide such functionality. Those are Atomikos, JOTM and Jencks. I will be using Jencks despite the fact that the library development seems to be discontinued since 2007, and it's not supported in any way. The reason is simply that it's the only library I've managed to configure by now. The even-more-outdated Jackrabbit-JTA example uses Jencks as well.

To deal with the problem of configuration, I've created a simple web application called “Wijax” (as I'm using Wicket for presentation, so it's Wicket-Jackrabbit-Spring). You can get the source of it in a form of a NetBeans project here.

Once you have your minimal Wicket-Spring application up and running, it's time to add some JCR.

Dependencies



If you haven't yet discovered the magic and pain of using Maven (like yours truly here), you will need the following libraries:


Jackrabbit-related JARs




  • Concurrent

  • Commons-Collections

  • Commons IO

  • Derby

  • Jackrabbit-API

  • Jackrabbit-JCA

  • Jackrabbit-Core

  • Jackrrabbit-JCR-Commons

  • Jackrrabbit-Text-Extractors

  • Jackrrabbit-SPI

  • Jackrrabbit-SPI-Commons

  • Lucene Core


All Jackrabbit-related jars might be found in Jackrabbit-Webapp distribution.

Jencks





For the latter two links I must thank Les Hazlewood who pointed those out at the Jencks discussion forum, potentially saving me a couple of hours of searching. This is not all, however. Jencks depends on JTA and JCA packages, and they are included in Jencks-All package. However, in the version of Jencks 2.1 they are both outdated as versions 1.0 of both libraries are included, when they are supposed to be 1.1 and 1.5 respectively. I just replaced whatever was found in javax.* folders included in Jencks-All by required versions.

Spring



You also might want to get AspectJWeaver in case you will be using AOP-based Spring configuration (like I'm going to) and move Commons Logging from Tomcat lib folder to WEB-INF in case you have it there and experiencing logging problems.

Obviously, all the libraries mentioned above can be found in the Wijax project archive, but I provided links just so you might want to get newer versions of maintaned jars.

Business Objects


We'll have two business objects called “Dumb” and “Silly” with Dumb being stored in database and Silly kept in repository. Both will have an “id” and “version” parameters, however, we will only deal with the version.

Create MySQL database “wijax” and import this sql in it:

CREATE TABLE IF NOT EXISTS `dumb` (
`id` bigint(11) unsigned NOT NULL auto_increment,
`version` int(5) unsigned NOT NULL default '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;


For both objects, we'll have DAOs and DTOs.

Dumb DTO:


package ru.sadninja.wijax.dumb;

import java.io.Serializable;

/**
*
* @author sp
*/
public class DumbDto implements Serializable {

private final long serialVersionUID = 1L;

private long id;
private int version;

public DumbDto(long id) {
this.id = id;
}

/**
* @return the id
*/
public long getId() {
return id;
}

/**
* @return the version
*/
public int getVersion() {
return version;
}

/**
* @param version the version to set
*/
public void setVersion(int version) {
this.version = version;
}
}


Dumb DAO interface:


package ru.sadninja.wijax.dumb;

import java.io.Serializable;

/**
*
* @author sp
*/
public interface DumbDao extends Serializable {

public void save(DumbDto dto);

public void saveWithRollback(DumbDto dto);

public DumbDto load(long id);

}


Dumb DAO JDBC implementation:


package ru.sadninja.wijax.dumb;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
*
* @author sp
*/
public class DumbJdbcDao extends JdbcDaoSupport implements DumbDao {

private final static long serialVersionUID = 1L;

static Logger dLogger = Logger.getLogger(DumbJdbcDao.class.getName());

private final static String INSERT_DUMB_QUERY_STRING =
"insert into dumb (version) values (?)";

private final static String SAVE_DUMB_QUERY_STRING =
"update dumb set version=? where id=?";

private final static String LOAD_DUMB_BY_ID =
"select version from dumb where id=?";

@Override
public void save(DumbDto dumb) {
if (dumb == null || dumb.getId() == 0) {
insert(dumb);
} else {
Object[] param = new Object[] { dumb.getVersion(), dumb.getId() };
if (getJdbcTemplate().update(SAVE_DUMB_QUERY_STRING, param) == 0) {
insert(dumb);
}
}

}

@Override
public void saveWithRollback(DumbDto dumb) {
throw new RuntimeException("Whoops, runtime exception");
}

protected void insert(DumbDto dumb) {
Object[] param = new Object[] { dumb.getVersion() };
getJdbcTemplate().update(INSERT_DUMB_QUERY_STRING, param);
}

@Override
public DumbDto load(final long id) {
dLogger.info("reading dumb #" + id);
Object[] params = new Object[] { id };
DumbDto dumb = (DumbDto) getJdbcTemplate().query(LOAD_DUMB_BY_ID, params,
new ResultSetExtractor() {
public Object extractData(ResultSet rs)
throws SQLException, DataAccessException {
rs.next();
DumbDto dumb = new DumbDto(id);
dumb.setVersion(rs.getInt("version"));
return dumb;
}

});

dLogger.info("dumb #" + id + " is of version " + dumb.getVersion());

return dumb;
}
}


Silly DTO:


package ru.sadninja.wijax.silly;

import java.io.Serializable;

/**
*
* @author sp
*/
public class SillyDto implements Serializable {

private final long serialVersionUID = 1L;

private long id;
private int version;

public SillyDto(long id) {
this.id = id;
}

/**
* @return the id
*/
public long getId() {
return id;
}

/**
* @return the version
*/
public int getVersion() {
return version;
}

/**
* @param version the version to set
*/
public void setVersion(int version) {
this.version = version;
}

}


Silly DAO:


package ru.sadninja.wijax.silly;

import java.io.Serializable;

/**
*
* @author sp
*/
public interface SillyDao extends Serializable {

public void read();

public void save(SillyDto silly);

public void saveWithRollback(SillyDto silly);

}


Silly DAO JCR implementation:


/**
*
* @author sp
*/
public class SillyJcrDao extends JcrDaoSupport implements SillyDao {

private final static long serialVersionUID = 1L;
static Logger sLogger = Logger.getLogger(SillyJcrDao.class.getName());

@Override
public void read() {
sLogger.debug("reading...");
final Session session = getSession();
try {
Node root = session.getRootNode();
if (root.hasNode("silly")) {
sLogger.info("silly node present");
Node silly = root.getNode("silly");
if (silly.hasProperty("version")) {
sLogger.info("silly is of version "
+ silly.getProperty("version").getLong());
}
} else {
sLogger.info("silly node missing");
}
} catch (RepositoryException re) {
throw convertJcrAccessException(re);
}
}

@Override
public void save(SillyDto sillyDto) {
sLogger.debug("saving...");
final Session session = getSession();
try {
Node root = session.getRootNode();

Node silly;
if (root.hasNode("silly")) {
silly = root.getNode("silly");
} else {
sLogger.debug("silly node missing, create one");
silly = root.addNode("silly");
}
silly.setProperty("version", sillyDto.getVersion());
session.save();
sLogger.info("silly is now of version "
+ silly.getProperty("version").getLong());
} catch (RepositoryException re) {
throw convertJcrAccessException(re);
}
}

@Override
public void saveWithRollback(SillyDto sillyDto) {
throw new RuntimeException("Whoa, runtime exception!");
}

}


Now it gets interesting: a service which performs a transactions with both DAOs involved. I called it Creepy Service.

An interface:



package ru.sadninja.wijax.service;

import java.io.Serializable;

/**
*
* @author sp
*/
public interface CreepyService extends Serializable {

public void updateVersion(int n);

public void readVersion();

}



And the implementation:


package ru.sadninja.wijax.service;

import ru.sadninja.wijax.dumb.DumbDao;
import ru.sadninja.wijax.dumb.DumbDto;
import ru.sadninja.wijax.silly.SillyDao;
import ru.sadninja.wijax.silly.SillyDto;

/**
*
* @author sp
*/
public class CreepyServiceImpl implements CreepyService {

private final static long serialVersionUID = 2L;

private DumbDao dumbDao;
private SillyDao sillyDao;

@Override
public void updateVersion(int n) {

DumbDto dumb = new DumbDto(1);
dumb.setVersion(n);
dumbDao.save(dumb);

SillyDto silly = new SillyDto(1);
silly.setVersion(n);
sillyDao.save(silly);

if (n == 11) {
sillyDao.saveWithRollback(silly);
}

// dumbDao.saveWithRollback(dumb);
}

@Override
public void readVersion() {
dumbDao.load(1);
sillyDao.read();
}

public void setDumbDao(DumbDao dumbDao) {
this.dumbDao = dumbDao;
}

public DumbDao getDumbDao() {
return dumbDao;
}

/**
* @return the sillyDao
*/
public SillyDao getSillyDao() {
return sillyDao;
}

/**
* @param sillyDao the sillyDao to set
*/
public void setSillyDao(SillyDao sillyDao) {
this.sillyDao = sillyDao;
}
}


And a page we will call be calling all this from:


package ru.sadninja.wijax.web.page;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.springframework.dao.DataAccessException;
import ru.sadninja.wijax.service.CreepyService;
import ru.sadninja.wijax.silly.SillyDao;

/**
*
* @author sp
*/
public class SillyPage extends WebPage {

private final static long serialVersionUID = 2L;

@SpringBean(name="sillyDao")
SillyDao sillyDao;
@SpringBean(name="creepyService")
CreepyService creepyService;

private int version = 0;

public SillyPage() {
sillyDao.read();
add(new SillyForm());
}

public int getVersion() {
return version;
}

public void setVersion(int version) {
this.version = version;
}

class SillyForm extends Form {
public SillyForm() {
super("sillyForm", new CompoundPropertyModel(SillyPage.this));
add(new FeedbackPanel("feedback"));
add(new TextField("version"));
}

@Override
public void onSubmit() {
try {
creepyService.updateVersion(getVersion());
setResponsePage(SillyPage.class);
} catch (DataAccessException dae) {
error(dae.getMessage());
} catch (RuntimeException re) {
error("Got a transaction exception: " + re.getMessage());
}
}
}
}


HTML for the page:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<form wicket:id="sillyForm">
<div wicket:id="feedback"></div>
<div>
<input type="text" wicket:id="version" />
<input type="submit" value="write" />
</div>
</form>
</body>
</html>


Okay, here comes the sweetest part: Spring application context configuration. What we have to do is provide some sort of access to both data sources, make the accessing mechanisms aware of the global transaction context and configure our DAOs and Service to be transactional.

Here goes:

/WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">


<bean id="wijaxApplication" class="ru.sadninja.wijax.WijaxApplication" />

<bean id="jtaTransactionManager" class="org.jencks.factory.TransactionManagerFactoryBean"/>

<bean id="jrConnectionManager"
class="org.jencks.factory.ConnectionManagerFactoryBean">
<property name="transactionManager">
<ref local="jtaTransactionManager"/>
</property>
<property name="transaction" value="xa"/>
</bean>

<bean id="jdbcConnectionManager"
class="org.jencks.factory.ConnectionManagerFactoryBean">
<property name="transactionManager">
<ref local="jtaTransactionManager"/>
</property>
<property name="transaction" value="xa"/>
</bean>

<bean id="jdbcManagedConnectionFactory" class="org.jencks.tranql.DataSourceMCF">
<property name="driverName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/wijax"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>

<bean id="jdbcXaDataSource" class="org.jencks.factory.ConnectionFactoryFactoryBean">
<property name="managedConnectionFactory" ref="jdbcManagedConnectionFactory"/>
<property name="connectionManager" ref="jdbcConnectionManager"/>
</bean>

<bean id="repository" class="org.jencks.factory.ConnectionFactoryFactoryBean">
<property name="managedConnectionFactory"
ref="repositoryManagedConnectionFactory"/>
<property name="connectionManager" ref="jrConnectionManager"/>
</bean>

<bean id="repositoryManagedConnectionFactory"
class="org.apache.jackrabbit.jca.JCAManagedConnectionFactory">
<property name="homeDir" value="/var/jackrabbit2"/>
<property name="configFile" value="/var/jackrabbit2/repository.xml" />
</bean>

<bean id="jcrSessionFactory" class="org.springmodules.jcr.JcrSessionFactory">
<property name="repository" ref="repository"/>
<property name="credentials">
<bean class="javax.jcr.SimpleCredentials">
<constructor-arg index="0" value=""/>
<constructor-arg index="1" value="">
</constructor-arg>
</bean>
</property>
</bean>

<!-- The JCR Template. -->
<bean id="jcrTemplate" class="org.springmodules.jcr.JcrTemplate">
<property name="sessionFactory" ref="jcrSessionFactory"/>
<property name="allowCreate" value="true"/>
</bean>

<tx:advice id="txSillyAdvice" transaction-manager="jtaTransactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="sillyDaoOperation"
expression="execution(* ru.sadninja.wijax.silly.SillyDao.*(..))" />
<aop:advisor advice-ref="txSillyAdvice" pointcut-ref="sillyDaoOperation" />
</aop:config>

<bean id="sillyDao" class="ru.sadninja.wijax.silly.SillyJcrDao">
<property name="template" ref="jcrTemplate"/>
</bean>

<tx:advice id="txDumbAdvice" transaction-manager="jtaTransactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="dumbDaoOperation"
expression="execution(* ru.sadninja.wijax.dumb.DumbDao.*(..))" />
<aop:advisor advice-ref="txDumbAdvice" pointcut-ref="dumbDaoOperation" />
</aop:config>

<bean id="dumbDao" class="ru.sadninja.wijax.dumb.DumbJdbcDao">
<property name="dataSource" ref="jdbcXaDataSource"/>
</bean>

<tx:advice id="txCreepyAdvice" transaction-manager="jtaTransactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="creepyServiceOperation"
expression="execution(* ru.sadninja.wijax.service.CreepyService.*(..))" />
<aop:advisor advice-ref="txCreepyAdvice" pointcut-ref="creepyServiceOperation" />
</aop:config>

<bean id="creepyService" class="ru.sadninja.wijax.service.CreepyServiceImpl">
<property name="dumbDao" ref="dumbDao" />
<property name="sillyDao" ref="sillyDao" />
</bean>

</beans>


My two main sources of inspiration for the above config were excerpts from the Chapter Nine mentioned above, and an example of Jencks configuration for multiple datasources.

Finally, you might want to add log4j.xml to watch Spring driving the transactions.

Test-drive



That seems like it. If you've done everything right, accessing SillyPage for the first time will give the output message that silly node is missing. Try putting something except “11” (as that will throw an exception) in the form and submitting it. In the log, you should get something like:


234824 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [ru.sadninja.wijax.service.CreepyService.updateVersion]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
234825 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
234825 [http-8084-1] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update
234825 [http-8084-1] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [update dumb set version=? where id=?]
234825 [http-8084-1] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
234828 [http-8084-1] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Registering transaction synchronization for JDBC Connection
234830 [http-8084-1] DEBUG org.springframework.jdbc.core.JdbcTemplate - SQL update affected 1 rows
234831 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
234831 [http-8084-1] DEBUG ru.sadninja.wijax.silly.SillyJcrDao - saving...
234831 [http-8084-1] DEBUG ru.sadninja.wijax.silly.SillyJcrDao - silly node missing, create one
234832 [http-8084-1] INFO ru.sadninja.wijax.silly.SillyJcrDao - silly is now of version 4
234832 [http-8084-1] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
234832 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
234979 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [ru.sadninja.wijax.silly.SillyDao.read]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
234979 [http-8084-1] DEBUG ru.sadninja.wijax.silly.SillyJcrDao - reading...
234980 [http-8084-1] INFO ru.sadninja.wijax.silly.SillyJcrDao - silly node present
234980 [http-8084-1] INFO ru.sadninja.wijax.silly.SillyJcrDao - silly is of version 4
234980 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit


If you check your “dumb” table, you'll see a row with id=1 and version=4.

Now try and put “11” in the form on the SillyPage. You should get an error message on the page, and in the output console:


407268 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [ru.sadninja.wijax.service.CreepyService.updateVersion]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
407268 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
407268 [http-8084-1] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL update
407268 [http-8084-1] DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement [update dumb set version=? where id=?]
407268 [http-8084-1] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
407270 [http-8084-1] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Registering transaction synchronization for JDBC Connection
407271 [http-8084-1] DEBUG org.springframework.jdbc.core.JdbcTemplate - SQL update affected 1 rows
407272 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
407272 [http-8084-1] DEBUG ru.sadninja.wijax.silly.SillyJcrDao - saving...
407273 [http-8084-1] INFO ru.sadninja.wijax.silly.SillyJcrDao - silly is now of version 11
407274 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
407274 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating transaction failed - marking existing transaction as rollback-only
407274 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Setting JTA transaction rollback-only
407274 [http-8084-1] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
407274 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction rollback


Now, try to hit “Back” and reload the page. You'll see that the silly version in repository is still “4”:


539452 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [ru.sadninja.wijax.silly.SillyDao.read]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
539452 [http-8084-1] DEBUG ru.sadninja.wijax.silly.SillyJcrDao - reading...
539453 [http-8084-1] INFO ru.sadninja.wijax.silly.SillyJcrDao - silly node present
539453 [http-8084-1] INFO ru.sadninja.wijax.silly.SillyJcrDao - silly is of version 4
539453 [http-8084-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit

Mar 16, 2009

Configure Logging in Spring With Log4J

Configuring logging for Spring is easy as long as you keep in mind that anything related to classpath is a total mess.

First, you want to obtain commons-logging and log4j libraries, if you haven't yet put them in server's lib folder or added to the web application.

Second, create a /WEB-INF/log4j.xml file with the following content:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-4r [%t] %-5p %c %x - %m%n"/>
</layout>
</appender>

<root>
<priority value ="error" />
<appender-ref ref="console" />
</root>

<category name="org.springframework" additivity="false">
<priority value="debug" />
<appender-ref ref="console" />
</category>

</log4j:configuration>


In short, this tells to throw whatever is of error level from all sources, and info- and debug-level messages from anything in org.springframework package in stdout.

Then, add the following section to your web.xml:

  <context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>


As pointed out here, logger Listener must be declared before ContextLoaderListener.

Now, this seems to be enought, however, at this point many people (judging by the amount of complaints on Spring forums) face the problem of nothing of the described to work.

This was also the case for me, and the problem was in commons-logging.jar, which was sitting in %CATALINA_HOME%/lib folder. Once I removed it out of there and added commons-logging library to my web application, the System.out was full of Spring's debug messages.

Mar 9, 2009

Enable Debug Level For SLF4J

To my shame, I'm absolutely new to logging in Java, and so far I've gone as far as “you need to add this to enable debug-level logging in your application”:

log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n


The exact problem was that despite having that configuration, I actually didn't see any debug printouts emitted by one of the libraries I was using, just the info ones. The reason was that library used SLF4J and was bound to the SLF4J-Simple logger.

Since I had no time to go ahead and read through the whole SLF4J manual, what I did was removing slf4j-simple.jar and including slf4j2log4j.jar instead.

And all the debug messages were there.

Jan 19, 2009

symfony's image_tag(): Handle With Care

Turns out, symfony's image_tag() helper is a sucker for memory.

It takes more than 34 megabytes of memory to render a page displaying a list of about 500 rows, each accompanied by a set of four or five icons, each icon's <img> tag created by image_tag() helper, with additional parameters provided like this:

image_tag("some_image.png", "alt=Lovely, title=Rita")

With those images coded explicitly, rendering takes just 8.5MB. A leak, maybe?

Jan 9, 2009

Pidgin's “New IM” Tray Icon

I think Pidgin is really one of the best-designed applications for GNOME. It almost looks like its interface was done by Google: nothing extra, just what you need to send and recieve messages. No bluish colours though.

However, I was a bit surprised once, when changing DPI resolution of the system also changed the Pidgin's tray icon for new instant message from original to a smiley: . I actually connected those two events later, when I've found out that Pidgin uses four different sets of tray icons to suit the height of the tray panel.

Those are stored in /usr/share/pixmaps/pidgin/tray in four different folders related to the size of the icons. Turns out, the tray-new-im.png icon in 22x22 folder is different from icons in three other folders. There's actually a confirmed bug for that. But wait, it goes further.

Not only the 22x22 icon has a smiley instead of an orange dialogue-box, also «speech box part of tray-message.png is white, instead of having a bluish gradient like the others in 22”. That'd probably be okay with me if it was about an icon in some messy has-version-for-every-os java application like Azureus, but since we're talking about spotless purity of GNOME native app, that's truly an outrage.

Luckily for everyone, Linux has a very simple way to fix this. Just save an icon from the bug page (or you can use the one from the above, they are identical), and replace the one in /usr/share/pixmaps/pidgin/tray/22x22 with it.

Now you're good to go (well, except for the tray-message.png).

Dec 29, 2008

Unicode and preg_replace()

When you're dealing with PHP and finally start using unicode instead of whatever non-unicode encodings, you might find a some of your symbols being transformed into rubbish, especially when dealing with non-latin alphabet.

The answer is to use a “u” flag in the expression string:

$s = preg_replace('/\s/u', $s);

Dec 16, 2008

sfGuardPlugin: Database Prefix

With symfony, it's kinda funny to search for documentation, since it's widely distributed across wiki, book, and various external sources, like Propel website.

That is why when you need something small and easy, but you don't know how to do this, it's usually faster to try that yourself first, search for an answer later. This is actually not the way I prefer to do stuff, so I will share some experiences about how to do small stuff in symfony.

So, the problem is, I have a single database on my web hosting available. However, I came across a situation where I needed two separate sets of sfGuardUser-s. To achieve this, I had to go the usual way — add a prefix to the tables.

All I had to do was to add “prfx_” to all the tables mentioned in <project-name>/plugins/sfGuardPlugin/config/schema.yml and then rebuild stuff with propel:

symfony propel:build-all

or, if you want to keep your existing data, rename tables in database manually, and then...

symfony propel:build-sql
symfony propel:build-form
symfony propel:build-model

...and you're set.

Sep 3, 2008

Google Chrome

Ah, I just love this thing.

Here're just some of the pros things I really like:
  • Cougar-fast javascript;

  • Interface leaves it all for the pages — my normal setup of firefox only has address bar, search bar, tab bar, status bar;

  • It combines sweet feats from all the browsers: options menu was a lot like Firefox, expandable textareas/inputs are somewhat safarish, “most used pages” are similar to Opera, etc;

  • It seems smart (for instance: I don't need to install a separate 250KB extension to open links from tab in the only sane way — next to parent tab);

The only bad thing I've noticed so far is that I couldn't turn the spellchecker off. I can't wait to see this thing in Linux. Firefox is too slow, Opera is too ugly, Epiphany is too primitive. Although Chrome might also not be the cure: usually, many Google's apps work not very well in X. Like, Picasa trying to mimick Windows interface looks as ugly as Opera, Google Earth can be very unstable depending on version (probably due to 3D rendering issues). Maybe Chrome won't be following their steps on that.

Also, it's kinda funny to hear all the “this is Google's way of controlling you”, “Google's way of showing you ads”, “Google's way of collecting your personal information”. In the world, where ninety-five percent of decisions about what to eat, what to wear, what music to listen, what to do after work is dictated by the monster of society, where certain agencies can obtain whatever information they need about whatever person they're interested in about one hour, it's truly sick to blather about privacy in that way.

Aug 20, 2008

Wicket + Spring (with JDBC) Configuration

In the next several paragraphs of symbols, I'll explain how to configure Spring and Wicket frameworks to work together, and even how to use JdbcDaoSupport, Spring's nice and handy JDBC tool.

I prefer not to deal with automatic configurations generated by Wicket or Spring IDE plugins, but to create the application from scratch. I will be using the NetBeans IDE. In a nutshell, all we'll have to do, is download some libraries, put some of them in Tomcat's lib folder, the rest in the classpath, and make a couple of modifications to web.xml and applicationContext.xml files.


Libraries


First of all, you will need Wicket and Spring libraries. If you download Spring with all dependencies, most probably, you won't have to download anything else (except mysql-connector, if you don't already have it).

Specific jars are:

— aspectjrt-1.x.jar
— cglib-nodep-2.x.jar
— log4j-1.x.jar
— slf4j-api-1.x.jar
— slf4j-log4j12-1.x.jar
— spring-2.x.jar
— wicket-1.3.x.jar
— wicket-ioc-1.3.x.jar
— wicket-spring-1.3.x.jar
— wicket-spring-annot-1.3.x.jar

Those jars listed above you should add to the libraries of your NetBeans project.

These three you have to put in $CATALINA_HOME/lib folder. Don't put them in classpath unless you've got several hours to find out why doesn't anything work.

— commons-logging-1.1.x.jar
— ehcache-1.2.x.jar
— mysql-connector-java-5.1.x-bin.jar


context.xml


Hope you've already created a NetBeans project and selected a “Web Application” at certain point.

First thing to do, is describe a database resource so we could access it via JNDI. To do this, put in the META-INF/context.xml something like this:

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/Chicago">
<!-- database resource -->
<Resource
name="jdbc/Chicago"
auth="Container"
driverClassName="com.mysql.jdbc.Driver"
type="javax.sql.DataSource"

username="database_user"
password="database_pass"
url="jdbc:mysql://localhost/my_database"

maxWait="10000"
maxActive="60"
minIdle = "0"
maxIdle="30"

removeAbandoned="true"
removeAbandonedTimeout="120"

testOnBorrow="false"
testOnReturn="false"
testWhileIdle="true"
validationQuery="SELECT 1"
timeBetweenEvictionRunsMillis="59000"
minEvictableIdleTimeMillis="58000"
/>
</Context>


Such resource configuration will tell your servlet container (namely, Tomcat) to keep alive JDBC connections and reconnect to avoid IO exceptions when connection isn't being used for a long time.


applicationContext.xml


You will now have to create a WEB-INF/applicationContext.xml file for Spring configuration.

Here's what you might want to put there:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/Chicago"/>
</bean>

<!-- Wicket WebApplication setup -->
<bean id="wicketApplication" class="com.mycorp.chicago.ChicagoApplication">
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<bean id="userDaoTarget" class="com.mycorp.chicago.user.JdbcUserDao">
<property name="dataSource" ref="dataSource"/>
</bean>

<bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="proxyTargetClass" value="true"/>
<property name="target" ref="userDaoTarget" />
<property name="transactionAttributes">
<props>
<prop key="save">PROPAGATION_REQUIRED,-Exception</prop>
<prop key="*">PROPAGATION_REQUIRED,-Exception,readOnly</prop>
</props>
</property>
</bean>
</beans>


What we just did there,

a) configured a dataSource object which is used in JdbcDaoSupport, to lookup database resource via JNDI;

b) told Spring to use Acegi filter on HTTP requests (Acegi is Spring's security framework, we'll deal with it in the next episode);

c) created a proxy DAO bean.


web.xml



We now must ask container to invoke Spring filter on certain requests. This is what we do in WEB-INF/web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Lianet Application</display-name>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<filter>
<filter-name>Spring Application Factory Filter</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationFactoryClassName</param-name>
<param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>Spring Application Factory Filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

</web-app>



UserDao.java and JdbcUserDao.java


For Spring injection to work, we create an interface of the DAO and its JDBC implementation. These I will keep in com.mycorp.chicago.user package.

/*
* UserDao.java
*/

package com.mycorp.chicago.user;

import java.io.Serializable;

/**
*
*/
public interface UserDao extends Serializable /* UserDetailsService */ {

public void test();

}




/*
* JdbcUserDao.java
*/

package com.mycorp.chicago.user;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

/**
*
*/
public class JdbcUserDao extends JdbcDaoSupport implements UserDao {

public void test() {
getJdbcTemplate().query("SELECT name FROM my_table WHERE id=1", new RowMapper() {

public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
String name = rs.getString("name");
System.out.println("Got object name: " + name);
return null;
}

});
}

}


You probably have already got the idea that to have all the preceding work, you'll need a MySQL database “my_database” with a “my_table” table with the latter consisting of at least an integer `id` and a varchar `name` fields.


IndexPage.java


Now let's inject our brand new UserDao in some Wicket component. Say, an index page of the app. I'll put it in the com.mycorp.chicago.web.page package.


/*
* Index.java
*/

package com.mycorp.chicago.web.page;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.spring.injection.annot.SpringBean;
import com.mycorp.chicago.user.UserDao;

/**
* Main page of the application.
*/
public class IndexPage extends WebPage {

@SpringBean(name="userDao")
private UserDao userDao;


public IndexPage() {
add(new Label("sampleLabel", "This is a text which is also a model."));
userDao.test();
}
}



IndexPage.html


By default, HTML template is stored along with the java source.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<span wicket:id="sampleLabel">Sample label</span>
</body>
</html>



ChicagoApplication.java


Now all that is left is the actual WebApplication of Wicket.

/*
* ChicagoApplication.java
*/

package com.mycorp.chicago;

import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.spring.injection.annot.SpringComponentInjector;
import com.mycorp.chicago.web.page.IndexPage;

/**
*
*/
public class ChicagoApplication extends WebApplication {

@Override
public Class getHomePage() {
return IndexPage.class;
}

@Override
public void init() {
super.init();
addComponentInstantiationListener(new SpringComponentInjector(this));
}

}



Enjoy


That's it. Now all that is left is to hit F6 button and wait for results. If you get a ClassNotFound exception, you probably forgot to add certain library. Wicket runtime exceptions are usually easy to fix since Wicket is very verbose in development mode.

Aug 18, 2008

Jackrabbit on JBoss with JNDI & RMI

We recently decided to use JCR, specifically, it's Apache implementation, JackRabbit. What's already done, is a web application running on Tomcat servlet container. One of the most obvious ways to tie those two, is to deploy JackRabbit on JBoss and expose it via JNDI.

Configuring JBoss Server


First, you need to download JBoss Application Server. Its installation in Ubuntu is as easy as extracting the downloaded jar/zip to the directory where you want JBoss to sit, and setting the JBOSS_HOME variable to that directory in ~/.bashrc.

To deploy JackRabbit, you need to obtain jackrabbit-jca.rar and jackrabbit-rmi.jar from JackRabbit downloads page and jcr-1.0.jar.

Note: The .rar archive contains all JackRabbit dependencies, including concurrent.jar.

You will also need to download the jcr-ds.xml. Edit the file so the <rar-name>jackrabbit-jca.rar</rar-name> property would contain the actual name of the rar you've downloaded (I had jackrabbit-jca-1.4.rar) and the homeDir property would point to the directory where you want JackRabbit to store its stuff.

To complete deployment, you now need to put jackrabbit-jca.rar, jackrabbit-rmi.jar and jcr-ds.xml files to $JBOSS_HOME/server/default/deploy and jcr-1.0.jar to $JBOSS_HOME/server/default/lib.

Note: Apparently, you should be careful about renaming the jcr-ds.xml file. I tried to name it “jcr-ds-1.4.xml” and kept getting JackRabbit deployed incompletely. Once I renamed it back to “jcr-ds.xml”, everything went smoothly.

To start JBoss, do $JBOSS_HOME/bin/run.sh.

Configuring Client


Now you might wanna check your JCR repository. You will need to create a simple function I found in the mail archive:

private static Session getJackrabbitSession() throws RepositoryException {
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
env.put(Context.PROVIDER_URL, "jnp://localhost:1099");
env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");

try {
InitialContext ctx = new InitialContext(env);
ClientAdapterFactory adapter = new ClientAdapterFactory();
RemoteRepository rr;
rr = (RemoteRepository) ctx.lookup("jnp://localhost:1099/jcrServer");
Repository repository = adapter.getRepository(rr);
Credentials credJBoss = new SimpleCredentials("username", "password".toCharArray());
return repository.login(credJBoss);
} catch (NamingException ex) {
ex.printStackTrace();
}

return null;
}


For that code to work, you will need jcr-1.0.jar, jackrabbit-jcr-rmi.jar, jnp-client.jar and jboss-common.jar libraries in your classpath. The latter two can be found in $JBOSS_HOME/client and $JBOSS_HOME/lib respectively.

If you've done everything correct, and are lucky enough, the “first hops” from Jackrabbit introduction should work fine, with appropriate changes to obtaining Session object done.

Jul 29, 2008

Deploying Django Project on Ubuntu Server

I've recently had to deploy a pretty simple Django application on Ubuntu server, and here're a few tips for myself in the future doing this once again.

For starters, there's a Jeff Baier's tutorial which covers most steps. There're, however, a couple of things I had to learn the hard way.

First, if you wish to use a stable version of Django, instead of dealing with subversion and compilation, just install it from repositories:

$ sudo apt-get install python-django

If you do so, your admin media will be in the following directory:

/usr/share/python-support/python-django/django/contrib/admin/media

Second, in your .py files don't use relative imports. If you have a project named “project_one” and two applications, “app_one” and “app_two” applications, this will work on local django server, but won't work in production:

import app_one.Something

Instead, you must always do

import project_one.app_one.Something

Third, mind the paths to your media files. It's better to adjust them before deployment so they look the same on your development localhost and production server, otherwise you might have problems during deployment.

I'm mentioning this here because django's default recommended media/admin-media url configuration is somewhat doesn't makes this easier.

Finally, keep in mind that out-of-the-box MySQL databases are not UTF-8 which is not very good if you have any language in db besides English. Unless you won't unicode-enable your MySQL server before you create your tables structure, you will have to change encoding of all the tables after.

Pretty much that is it, which actually seems really smooth deployment process to me, so another score for Django.

Jun 3, 2008

MSSQL + PHP in Ubuntu 8.04

Following comments to this post, I got MS SQL working with PHP.

$ apt-get install freetds-dev tdsodbc php5-sybase
$ nano /etc/odbcinst.ini
[FreeTDS]
Description = FreeTDS 0.61-5 Deb
Driver = /usr/lib/odbc/libtdsodbc.so
Setup = /usr/lib/odbc/libtdsS.so
FileUsage = 1
CPTimeout = 5
CPReuse = 5


$ nano /etc/odbc.ini
[Products]
Description = Products on The MSSQL Server
Driver = FreeTDS
Servername = FSData
Database = Products
Port = 1433

May 21, 2008

A Lame Way To Clip Skin Highlights

I'm no professional or even advanced-amateur photographer, but sometimes people ask me to make photos. Since I do suck at that, and I don't have my own studio stuffed with lights, softboxes and stuff, I often take photos of people poorly lit with lots of highlights on their skin. There's a lame way to remove those.

I'm not sure if it's possible to achieve this using just curves or levels, here's how I do it.

1. Go to Levels and move the center slider to the right as far as neccessary so highlighted areas turn white and not-so-highlighted areas turn dark. Sometimes instead of moving center slider, you will need to gather black, white and center slide in the middle.

2. Increase Contrast until you have a dark background with islands of white highlighted areas. Sometimes background is completely black, and you can just delete it after magic wand selection. Sometimes you need to select white by colour and delete inverted selection.

3. Fill alhpa-selection of remaining areas with skin colour you can pick from not-highlighted parts. Depending on different parts of face illumination, you may want to fill highlighted areas with different colours.

4. The next is up to blur and opacity tuning.

May 1, 2008

Ubuntu 8.04 Wireless

I googled out the wireless/ndiswrapper problem. The solution, quoting Ankur Srivastava's blog post, is as simple as this:

$ nano /etc/rc.local
then put
rmmod ssb
modprobe ndiswrapper

before exit 0...
...then
$ shutdown -r now

And your wireless is there.

Update: Apparently, these actions aren't needed if you're using kernel 2.6.24-17. After update from 8.04 original 2.6.24-16, wireless had not worked until I removed those lines from rc.local.

Apr 24, 2008

Last Insert Id Without Extra SELECT in Spring

If you have a table with auto_increment for an id, you often need to get this id immediately after a new instance has been created.

The easy way to do this is to perform a SELECT LAST_INSERT_ID() query. However, most of the time you want as less database requests as possible. There is a way to avoid LAST_INSERT_ID() code using Spring's JDBC support.


final String DEF_INSERT_USER_QUERY = "insert into users (username, password) values (?,?)";
final String firstname = createdUser.getFirstname();
final String lastname = createdUser.getLastname();

KeyHolder keyHolder = new GeneratedKeyHolder();
getJdbcTemlpate().update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection)
throws SQLException {
PreparedStatement ps = connection.prepareStatement(DEF_INSERT_USER_QUERY,
new String[] {"firstname", "lastname"});
ps.setString(1, firstname);
ps.setString(2, lastname);
return ps;
}
}, keyHolder);

createdUser.setId(keyHolder.getKey().intValue());