How to reuse a java.lang.StringBuilder

It’s common knowledge in Java that you want to clear a StringBuilder object, and not recreate it. Why? GC for instance. But, surprisingly enough, there is no clear() method on the StringBuilder class, and so we resort to manual clear methods. The ones I’ve seen are:

  • new StringBuilder which basically recreates the object
  • setLength(0) which changes the internal flags of the StringBuilder
  • delete(0,sb.length()) which deletes the whole buffer

One would think that the best technique is setLength – but I’ve decided to give it a short test.
Here is the code I’ve used:

package com.tona;

import java.util.Random;

public class TestStringBuffer {
	private static final int NUM_OF_RUNS = 100000;
	private static final int MAX_LENGTH = 1000;
	private static Random rand = new Random();

	private static int fillString(StringBuilder sb,boolean isSameLenString) {
		
		int length;
		
		if (isSameLenString)
			length = MAX_LENGTH;
		else 
			length = rand.nextInt(MAX_LENGTH);
		
		for (int i = 0; i < length; ++i) 
			sb.append("a");
		
		return length;
	}
	
	private void runBenchmark(boolean isSameLenString) {
		StringBuilder sb = new StringBuilder();
		long start = System.currentTimeMillis();

		for (int i = 0; i < NUM_OF_RUNS; ++i) {
			fillString(sb,isSameLenString);
			sb.setLength(0);
		}
		long end = System.currentTimeMillis();
		long diffWithLen0 = end - start;
		
		start = System.currentTimeMillis();

		for (int i = 0; i < NUM_OF_RUNS; ++i) {
			fillString(sb,isSameLenString);
			sb = new StringBuilder();
		}
		end = System.currentTimeMillis();
		long diffWithNew = end - start;

		start = System.currentTimeMillis();

		for (int i = 0; i < NUM_OF_RUNS; ++i) {
			fillString(sb,isSameLenString);
			sb = new StringBuilder(MAX_LENGTH);
		}
		end = System.currentTimeMillis();
		long diffWithNewConstLength = end - start;

		start = System.currentTimeMillis();

		for (int i = 0; i < NUM_OF_RUNS; ++i) {
			fillString(sb,isSameLenString);
			sb.delete(0, sb.length());
		}
		end = System.currentTimeMillis();
		long diffWithDelete = end - start;

		start = System.currentTimeMillis();

		for (int i = 0; i < NUM_OF_RUNS; ++i) {
			int length = fillString(sb,isSameLenString);
			sb.delete(0, length);
		}
		end = System.currentTimeMillis();
		long diffWithDeleteConstLength = end - start;

		System.out.println("With setLength(0) " + diffWithLen0);
		System.out.println("With new StringBuilder() " + diffWithNew);
		System.out.println("With new StringBuilder(MAX_LENGTH) " + diffWithNewConstLength);
		System.out.println("With delete(0, sb.length()) " + diffWithDelete);
		System.out.println("With delete(0, length) " + diffWithDeleteConstLength);
	}
	
	public static void main(String[] args) {
		TestStringBuffer test = new TestStringBuffer();
		System.out.println("Constant length string");
		test.runBenchmark(true);
		System.out.println("Changing length string");
		test.runBenchmark(false);
		
	}
}

And here are the results:

Constant length string
With setLength(0) 1524
With new StringBuilder() 1501
With new StringBuilder(MAX_LENGTH) 1365
With delete(0, sb.length()) 1369
With delete(0, length) 1391
Changing length string
With setLength(0) 686
With new StringBuilder() 743
With new StringBuilder(MAX_LENGTH) 796
With delete(0, sb.length()) 715
With delete(0, length) 698

(Note that changing string length uses a Random, so results may vary).
With StringBuilder object with a fixed length, the new StringBuilder with the predefined length is the best option, although not much slower than the delete method. With the varying length strings, setLength and delete and more of less on par (give it a few runs and you’ll see it for yourself).
So, now you know how to clean your StringBuilder object. Enjoy.

Streaming movies from my Linux laptop to XBox

Hi

I have an XBox machine at home, and it’s great. I have a Linux based laptop – and it’s also great. However, during my Windows days I was able to use Windows Media Center to stream movies to my XBox – and I wanted to do that on my Linux box too.

Not too difficult.
Just read this article. Easy. However, it uses ethernet cable to connect XBox and Linux. Well, that’s no longer needed. Just replace, in the ushare.conf file the USHARE_IFACE=etho line with USHARE_IFACE=wlan0. That was enough.

Enjoy.

Connecting iReport to Vertica

I’m continuing my journey of connecting JasperReports and Vertica, and it’s not an easy task.
The Vertical team did some damage – they changed their JDBC driver class name (it used to be com.vertica.Driver and now it com.vertica.jdbc.Driver), so the iReport application doesn’t let you connect to the Vertica database, since you constantly get a ClassNotFoundException.
Have no fear – it’s easy to fix.

  1. Add the Vertica JDBC driver JAR to the iReport ClassPath. It can be done under Tools|Options–>ClassPath. Just click the “Add JAR” button and map it to the location of your JAR file
  2. Create a new XML file, with the following data (update to your environment):
    [xml]
    <?xml version=”1.0″?>
    <iReportConnectionSet>
    <iReportConnection name=”Vertica” connectionClass=”com.jaspersoft.ireport.samples.db.SampleDatabaseConnection”>
    <connectionParameter name=”ServerAddress”><![CDATA[IP_ADDR]]></connectionParameter>
    <connectionParameter name=”SavePassword”><![CDATA[true]]></connectionParameter>
    <connectionParameter name=”Url”><![CDATA[jdbc:vertica://IP_ADDR:5433/DB_NAME]]></connectionParameter>
    <connectionParameter name=”JDBCDriver”><![CDATA[com.vertica.jdbc.Driver]]></connectionParameter>
    <connectionParameter name=”Database”><![CDATA[DB_NAME]]></connectionParameter>
    <connectionParameter name=”Password”><![CDATA[PASSWORD]]></connectionParameter>
    <connectionParameter name=”Username”><![CDATA[dbadmin]]></connectionParameter>
    </iReportConnection>
    </iReportConnectionSet>
    [/xml]
  3. Create a new Data Source. Click the Report Datasources icon, and instead of new, click import and choose the XML file you created.

Your report should now work with Vertica.

Connecting JasperReports to Vertica

I have a customer that asked me to build a Vertica environment and connect it to JasperReports. Fun!
So, it took a while (mostly due to technical difficulties on my end), but it works. And I thought I’d document how.

  1. Install Vertica. That’s an easy task, lots of documentation on the Vertica site. Frankly – I haven’t read any of it. Just clickty-click and you’re done.
  2. Create the Vertica database. Use /opt/vertica/bin/adminTools for that. Make sure you run it under the dbadmin user (use su - dbadmin if you have problems to login directly).
  3. Install Tomcat. I used Tomcat 6, but 7 is supported too.
  4. Install JasperReports Server (can be downloaded here). I used the WAR file version (had problems with the X Windows support over SSH) – see installation documentation here. This was not the easiest thing to do.
  5. Copy the Vertica JDBC driver to Tomcat (from /opt/vertica/java/lib/vertica_5.1.1_jdk_5.jar to TOMCAT_HOME/lib)
  6. Start Tomcat (using TOMCAT_HOME/bin/startup.sh)
  7. Log in to the Jasper Reports application at http://your_host:8080/jasperreports. Default username is jasperreports and the password is jasperreports too.
  8. Create a new DataSource in Jasper. For driver class enter com.vertica.jdbc.Driver. For URL enter jdbc:vertica://vertica_host:5433/db_name. User is dbadmin and password is the database password you entered when you created the database
  9. update

    Turns out I was overly optimistic. Although Jasper UI shows the connection works, reports can’t run with this connection. You need to use a JNDI Data Source connection. Easy to do:

    1. Open the context.xml file in the TOMCAT_HOME/webapps/jasperreports/META-INF
    2. Add the following lines:
      <Resource name="jdbc/vertica" auth="Container" type="javax.sql.DataSource"
      maxActive="100" maxIdle="30" maxWait="10000"
      username="dbadmin" password="test"
      driverClassName="com.vertica.jdbc.Driver"
      url="jdbc:vertica://localhost:5433/test" />
    3. Now create a JNDI based data source in Jasper (see here for more details)
    4. I have also saw that the TOMCAT_HOME/conf/Catalina/default/jasperreports.xml file needs to be updated, and contain the same code snippet from above. I have no clue why Tomcat doesn’t update this file automatically.
    5. Restart the Tomcat server

That’s it. Hope it helped 😉

Billing software evaluation

I’m doing a new project for a customer, who wants me to write the licensing/billing module in his PHP app. So, the first thing is to decide on the billing software to use. I came up with this table (kind of a braindump thing), trying to evaluate the different solutions I found.

Updated

# Item PayPal Chargify Zuora Plimus
1. API
2. Charge via API V V V V
3. Subscription registration via API V V V V
4. Allow changes in subscription plan/price via API ? V V V
5. Get billing information (invoices) via API V V V V
6. Notifications V (API) V- (not API, but can be automatic) V (API) ?
7. Send invoices to customers V V V V
8. Billing management
9. Discounts V V V
10. Chargeback V V V V
11. Custom workflows for issue management X X V ?
12. Prepay discounts V- Manual
13. Accept credit cards V V V V
14. Environment
15. Free test account V V V V
16. Site stores customer payment information V V V V
17. Integration with SF X X V X
18. Pricing 2.9% + 0.30c per transaction 99$/month (depend on number of customers), additions for gateways Not published 8% per transaction
19. Customer self service portal V V V V

What do you think? Any major player I’ve left out? Anything I should add to my criteria?

Update

I’ve started with PayPal – they have terrible documentation and API. Took me a day to actually do something – but it worked. Only then I discovered that subscriptions require a PayPal account. So I’m now evaluating Zuora. I’ll post my experience with them in future posts.

Improving authorization performance in Liferay

I have recently run a benchmark on a Liferay 6.1 portal. Most of the bottlenecks were the result of the portlets themselves, but a major point was the authorization mechanism of Liferay.
The way Liferay authorization works is that for every component on the screen an authorization check is made. Now, this makes sense. But the problem is that the default implementation is a bit naive – it goes to the database to check if the user has the correct authorization. And since rights can be inherited – each check goes multiple times to the database.
So, I set out to solve this irritating issue by using a cache. Now – this cache is naive too – if a user was granted a new role – a server restart is needed. But this implementation is easy to extend.

  1. Create a new class, that extends com.liferay.portal.security.permission.AdvancedPermissionChecker
  2. Open the portal-ext.properties file.
  3. Add the property permissions.checker={your class name} to it
  4. The class code should look like this:
    [java]
    package com.tona.liferay.permission;

    import java.util.HashMap;
    import java.util.Map;

    public class TonaPermissionChecker extends
    com.liferay.portal.security.permission.AdvancedPermissionChecker {

    private static Map permissionCache = new HashMap();

    public boolean hasPermission(long groupId, String name, String primKey,
    String actionId) {

    PermissionKey key = new PermissionKey(groupId, name, primKey, actionId);
    if (permissionCache.containsKey(key)) {
    return permissionCache.get(key);
    }

    boolean result = super.hasPermission(groupId, name, primKey, actionId);
    permissionCache.put(key, result);

    return result;

    }

    private class PermissionKey {
    private long groupId;
    private String name;
    private String primKey;
    private String actionId;

    public PermissionKey(long groupId, String name, String primKey,
    String actionId) {
    super();
    this.groupId = groupId;
    this.name = name;
    this.primKey = primKey;
    this.actionId = actionId;
    }

    public long getGroupId() {
    return groupId;
    }

    public void setGroupId(long groupId) {
    this.groupId = groupId;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public String getPrimKey() {
    return primKey;
    }

    public void setPrimKey(String primKey) {
    this.primKey = primKey;
    }

    public String getActionId() {
    return actionId;
    }

    public void setActionId(String actionId) {
    this.actionId = actionId;
    }

    @Override
    public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
    + ((actionId == null) ? 0 : actionId.hashCode());
    result = prime * result + (int) (groupId ^ (groupId >>> 32));
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    result = prime * result
    + ((primKey == null) ? 0 : primKey.hashCode());
    return result;
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj)
    return true;
    if (obj == null)
    return false;
    if (getClass() != obj.getClass())
    return false;
    PermissionKey other = (PermissionKey) obj;
    if (actionId == null) {
    if (other.actionId != null)
    return false;
    } else if (!actionId.equals(other.actionId))
    return false;
    if (groupId != other.groupId)
    return false;
    if (name == null) {
    if (other.name != null)
    return false;
    } else if (!name.equals(other.name))
    return false;
    if (primKey == null) {
    if (other.primKey != null)
    return false;
    } else if (!primKey.equals(other.primKey))
    return false;
    return true;
    }
    }
    }
    [/java]

  5. Package the class in a JAR file, and put in TOMCAT_HOME/webapps/ROOT/WEB-INF/lib