Saturday, March 31, 2012

Confluence 3.x and com.atlassian.confluence.content.render.xhtml

com.atlassian.confluence.content.render.xhtml is a package from Confluence 4, responsible for rendering new XHTML macro. If you compile your plugin for Confluence 4 and your plugin has <xhtml-macro/>, then you will get an import of com.atlassian.confluence.content.render.xhtml in your MANIFEST.MF. Something like this:

com.atlassian.confluence.content.render.xhtml;version="0.0"

So far, so good. But what happens, if you try to install your plugin in confluence 3.x?


org.osgi.framework.BundleException: Unresolved constraint in bundle com.skype.confluence.skype-bth [72]: Unable to resolve 72.0: missing requirement [72.0] package; (&(package=com.atlassian.confluence.content.render.xhtml)(version>=0.0.0))


The reason is obvious, com.atlassian.confluence.content.render.xhtml is missing in confluence 3.x, but we are asking for it. Even though, we are not going to use it. We might have separate <macro/> definition, that does not need XHTML.

Solution is to tell Felix, that com.atlassian.confluence.content.render.xhtml is optional:

<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-confluence-plugin</artifactId>
<version>3.8</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
com.atlassian.confluence.content.render.xhtml;resolution:=optional;version="0.0.0",
*;version="0.0"
</Import-Package>
</instructions>
</configuration>
</plugin>
view raw pom.xml hosted with ❤ by GitHub


Friday, March 30, 2012

JNDI datasource in confluence plugin

It appears, that Confluence dev documentation completely lacks information, regarding accessing third party databases.

The only example I could find is the source of SQL Plugin. However, it seemed to be too low level and I would prefer to use Spring to handle it. Here comes my solution:

<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd"
default-autowire="autodetect">
<jee:jndi-lookup
id="bthDatasource"
jndi-name="java:comp/env/jdbc/bthDatasource"
proxy-interface="javax.sql.DataSource"
lookup-on-startup="false" />
<bean id="bthJdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg ref="bthDatasource"/>
</bean>
</beans>
view raw bean.xml hosted with ❤ by GitHub


You can see here, that I'm using lazy look up with proxy interface. I had to do it, because otherwise look up happens when plugin is installed or activated and for some reason, JNDI datasource that I defined was not visible to that thread.

Unfortunately, OSGI bundle makes it a bit tricky, as some required classes will be missing and you have to instruct Felix to import them:

<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-confluence-plugin</artifactId>
<version>3.8</version>
<extensions>true</extensions>
<configuration>
<productVersion>${confluence.version}</productVersion>
<productDataVersion>${confluence.data.version}</productDataVersion>
<instructions>
<Import-Package>
org.springframework.aop;version="0.0.0",
org.springframework.aop.framework;version="0.0.0",
org.aopalliance.aop;version="1.0",
javax.sql;version="0.0.0",
*;version="0.0"
</Import-Package>
</instructions>
</configuration>
</plugin>
view raw pom.xml hosted with ❤ by GitHub

Wednesday, February 29, 2012

Populating Oracle v$session in Spring web app

Our project heavily relies on Oracle PL/SQL procedures. Those procedures are used by different applications and database developers always wanted to know two things:

1) Which application is calling the procedure
2) Who is currently logged into the application

After investigating the topic a bit, I've found OracleConnection.setEndToEndMetrics method in oracle JDBC driver. Using this method, you can populate some fields in v$session view, including v$session.client_identifier and v$session.module. In our case, logged in user goes to client_identifier and calling application to module.

There are already some samples of setting client identifier using this method, but I found most of them incomplete. Here comes another one:

package my.package;
import static oracle.jdbc.OracleConnection.END_TO_END_CLIENTID_INDEX;
import static oracle.jdbc.OracleConnection.END_TO_END_MODULE_INDEX;
import static oracle.jdbc.OracleConnection.END_TO_END_STATE_INDEX_MAX;
import static org.springframework.util.StringUtils.hasText;
import oracle.jdbc.OracleConnection;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.SQLException;
@Aspect
@Component
public class OracleConnectionMetricsSettingAspect {
private static final String APPLICATION_NAME = "MY_APP";
private SecurityContext securityContext;
@AfterReturning(
pointcut = "execution(* javax.sql.DataSource.getConnection(..))",
returning = "connection")
public void setMetrics(Connection connection) throws SQLException {
Connection metaDataConnection = connection.getMetaData().getConnection();
if (!(metaDataConnection instanceof OracleConnection)) {
return;
}
String[] metrics = new String[END_TO_END_STATE_INDEX_MAX];
if (hasText(getLoggedInUser())) {
metrics[END_TO_END_CLIENTID_INDEX] = getLoggedInUser();
}
metrics[END_TO_END_MODULE_INDEX] = APPLICATION_NAME;
((OracleConnection) metaDataConnection).setEndToEndMetrics(metrics, (short) 0);
}
private String getLoggedInUser() {
Authentication authentication = securityContext.getAuthentication();
if (authentication == null) {
return null;
}
return authentication.getName();
}
@Autowired
public void setSecurityContext(SecurityContext securityContext) {
this.securityContext = securityContext;
}
}


You can see here, that we are using AOP to intercept javax.sql.DataSource.getConnection() methods and populate all connections with logged in user from Spring security SecurityContext. Module is just a constant.