ICE09 . playing with java, scala, groovy and spring .

Playing with Spring

Archive for June, 2009

Eclipse Galileo: Memory Analyzer (MAT) …and a little Spring…

Posted by ice09 on June 28, 2009

The source can be downloaded here. The Spring libraries have to be added manually to the local maven repo (compare this.) The pom is inspired by this post.

I wanted to play with Eclipse Galileo and the Memory Analyzer (MAT), which I used successfully some time ago to identify possible memory leaks.

Using an old example, I construed a case where instead of using a singleton, accidentially a prototype is used (once again a nice sample for Spring’s capabilities – changing one line changes the behaviour completely).

<?xml version="1.0" encoding="UTF-8"?>
<beans &#91;...&#93;>
               
<!-- using is the "correct" singleton pattern
     <bean id="scopetest" class="com.commons.Scopetest">
     </bean>
-->

<!-- using is the "leaking" prototype pattern
     <bean id="scopetest" class="com.commons.Scopetest" scope="prototype">
         <aop:scoped-proxy/>  
     </bean>
-->
     
     <bean id="testclass" class="com.commons.Testclass">
     	<property name="scopetest" ref="scopetest"/>
     </bean>
     
</beans>

What’s the difference? Let’s have a look at the main class:

Testclass testclass = (Testclass) app.getBean(“testclass”);
for (; i < 1000000; i++) { Scopetest proxy = testclass.getScopetest(); leakingSet.add(proxy.uniqueCode()); } System.out.println("size: " + leakingSet.size()); [/sourcecode] Using a singleton, the uniqueCode is always the same (I did not use hashCode since it collides with the Proxy hashCode), which leads to a Set with just one object (the uniqueCode of the singleton - which is always the same). Using the prototype, the class is proxied by cglib and each call to uniqueCode() creates a new Scopetest object, each has a timestamp-based uniqueCode - therefore each object (the uniqueCode-Integer) is added to the Set. As being said, this is highly construed, however, if you forget to change the object type from prototype to singleton somewhere in your application, you might experience a non-expected behaviour (eg. 1000000 objects instead of one). How can this be found with Eclipse MAT? Preparation

First, Eclipse MAT has to be installed (anybody knows why?):


Afterwards, the sample application is started and a heap dump is taken. There are several options for doing this, I used jvisualvm.


Usage

Back in Galileo, the heap dump can now be opened with Eclipse MAT (after opening the MAT Perspective):




Using the generated report, the source of the memory leak is quite easy to find – it is immediately identified as a “suspect” automatically by the “Leak Suspect Report”. Even the correct name of the private member (the Set with the 1000000 objects) is shown (leakingSet), which makes pinpointing the causing object an ease.

Advertisements

Posted in Eclipse Galileo | Tagged: | 1 Comment »

Google App Engine, GWT, Spring 3, JPA and Google Spreadsheet

Posted by ice09 on June 7, 2009

If you are interested in Spring 3 on the GAE, there is a newer post about just this topic (and some REST!).

The previous post describes the main steps, this post just extends two classes and adds a “sync function”, which allows for syncronizing with data im a Google Spreadsheet.

The github project is here.

Caveats

  • There is a known problem with cookie handling, therefore the static block is used.
  • The spreadsheet document must be named tabledata.


  • The sample is quite stupid, to test the sync function, the usename sync must be used.

GreetingsServiceImpl.java

package com.commons.ssheet.server;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.stereotype.Component;

import com.commons.ssheet.client.GreetingService;
import com.google.gdata.client.http.GoogleGDataRequest;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.SpreadsheetFeed;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.util.ServiceException;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

@SuppressWarnings("serial")
@Component
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {

 	static {
		System.setProperty(GoogleGDataRequest.DISABLE_COOKIE_HANDLER_PROPERTY, "true");
 	}
	
	private boolean first;

	@Autowired
	private EntityManager entityManager;
	@Autowired
	private SpreadsheetService spreadsheetService;
	@Autowired
	private URL metafeedUrl;
	
	private String username = "YOURUSERNAME";
	private String password = "YOURPASSWORD";
	
	public JpaTemplate getTemplate() {
		return new JpaTemplate(entityManager);
	}
	
	public String greetServer(String input) {
		try {
			return getData(input);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ServiceException e) {
			e.printStackTrace();
		}
		return null;
	}

	private String getData(String input) throws IOException, ServiceException {
		String all = "";
		if (input.equals("sync")) {
			first = false;
		}
		for (Customer customer : getCustomers()) {
			if (!first) {
				removeCustomer(customer);
			}
		}
		first = true;
		persistCustomer(createCustomer(input));
		if (input.equals("sync")) {
			for (Customer customer : syncWithSpredsheet()) {
				persistCustomer(customer);
			}
		}
		for (Customer customer : getCustomers()) {
			all += "id: " + customer.getId() + " - firstname: " + customer.getFirstName() + " - name:" + customer.getLastName() + "<br>";
		}
		return all;
	}

	private void removeCustomer(Customer customer) {
		EntityTransaction trx = entityManager.getTransaction();
		trx.begin();
		entityManager.remove(customer);
		trx.commit();
	}

	private Customer createCustomer(String input) {
		Customer newCustomer = new Customer();
		newCustomer.setFirstName(input + System.currentTimeMillis());
		newCustomer.setLastName(input + System.currentTimeMillis());
		return newCustomer;
	}
	
	private void persistCustomer(Customer customer) {
		EntityTransaction trx = entityManager.getTransaction();
		trx.begin();
		entityManager.persist(customer);
		trx.commit();
	}

	private Collection<Customer> getCustomers() {
		return getTemplate().execute(new JpaCallback<Collection<Customer>>() {
			@Override
			public Collection<Customer> doInJpa(EntityManager arg0) throws PersistenceException {
				return (Collection<Customer>) entityManager.createQuery("SELECT cust FROM com.commons.ssheet.server.Customer cust").getResultList();
			}
		});
	}
	
	private List<Customer> syncWithSpredsheet() throws IOException, ServiceException {
		spreadsheetService.setUserCredentials(username, password);
		List<Customer> result = new ArrayList<Customer>();
		SpreadsheetFeed feed = (SpreadsheetFeed) spreadsheetService.getFeed(metafeedUrl, SpreadsheetFeed.class);
		List<SpreadsheetEntry> spreadsheets = feed.getEntries();
		for (int i = 0; i < spreadsheets.size(); i++) {
			SpreadsheetEntry entry = spreadsheets.get(i);
			if (entry.getTitle().getPlainText().trim().equals("tabledata")) {
				List<WorksheetEntry> worksheets = entry.getWorksheets();
				for (int j = 0; j < worksheets.size(); j++) {
					WorksheetEntry worksheet = worksheets.get(j);
					URL cellFeedUrl = worksheet.getCellFeedUrl();
					CellFeed cfeed = spreadsheetService.getFeed(cellFeedUrl, CellFeed.class);
					Customer customer = new Customer();
					boolean wasAdded = true;
					for (CellEntry cell : cfeed.getEntries()) {
						String value = cell.getCell().getInputValue();
						if (cell.getTitle().getPlainText().startsWith("A")) {
							if (!wasAdded) {
								result.add(customer);
								customer = new Customer();
							}
							customer.setFirstName(value);
						} else if (cell.getTitle().getPlainText().startsWith("B")) {
							customer.setLastName(value);
							wasAdded = false;
						}
					}
					result.add(customer);
				}
			}
		}
		return result;
	}

}

dispatcher-servlet.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="com.commons" />
	
	<bean id="spreadsheetService" class="com.google.gdata.client.spreadsheet.SpreadsheetService">
		<constructor-arg><value>appname</value></constructor-arg>
	</bean>
	
	<bean id="metafeedUrl" class="java.net.URL">
		<constructor-arg><value>http://spreadsheets.google.com/feeds/spreadsheets/private/full</value></constructor-arg>
	</bean>

	<bean id="entityManager" factory-bean="EMF" factory-method="entityManager" />

	<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="mappings">
			<value>
				/googlespreadsheet/greet=gwtController
			</value>
		</property>
	</bean>
	
  	<bean name="gwtController" class="com.commons.ssheet.server.GWTController">
    	<property name="remoteService" ref="greetingServiceImpl"/>
  	</bean>	

</beans>

Posted in Google App Engine, Google Spreadsheet API, GWT | 5 Comments »