Thursday, March 21, 2013

jQuery Masonry Tips

jQuery Masonry : http://masonry.desandro.com/index.html

If call ajax and render each elements dynamically by javascrit, need to call 'appended' method. but sometimes the elements only to be shown on single column.
if need to show elements with multiple columns,
set width first
$("#div").masonry({columnWidth: 310});


 var box = renderSingleElement(item);
 var $singlebox = $(box);
 $("#div_single_element").append($singlebox).masonry( 'appended', $singlebox);

take note if set column width as 310, the width of single element should not be greater than 285, otherwise the elements still be in one column, not multiple columns, so take care to set the element width and column width.


expose URL to access local files on server


Sometimes, we need to access local files on server via HTTP URL. If the files are under /webapp/<project>, take tomcat as an example, it is very east to access them via relative path. If the files are outside of tomcat or other web app servers, how to resolve it?

1) create soft link at /var/www/html/<your root context>


ln -s <file location on server> imgupload


2) modify apache setting

<Directory />
Options FollowSymlinks
Allow from all
</Directory>

refer to http://httpd.apache.org/docs/2.2/mod/core.html

3) then on web page
<img src="/imgupload/<subfolder if have>/abc.jpg" />

Tuesday, March 5, 2013

use JSTL to render image blob

1) on DB level, use byte[] to present image

private byte[] image

2) on servlet level, encode byte[] with Base64

String imageBase64=Base64.encode(image);

3)

<img src="data:image/jpg;base64, ${base64Image} "  height="152" width="267"/>


Friday, February 22, 2013

Spring AOP Logging Example

In general, we print the input arguments and output response for debugging. If we add log statements in each methods, it is very troublesome, spring provides AOP make it easy.

1) add this configuration

<aop:aspectj-autoproxy />

2)


@Component
@Aspect
public class ServiceLoggingAspect extends ServiceBase {

@Around("execution(* com.abc.service.impl.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {

Long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
info("[" + joinPoint.getTarget().getClass().getName() + "]."
+ joinPoint.getSignature().getName(),
" return " + result + " with arguments : "
+ Arrays.toString(joinPoint.getArgs())
+ ", Time to execute : "
+ (System.currentTimeMillis() - startTime) + "ms");
return result;
} catch (Exception e) {
error("[" + joinPoint.getTarget().getClass().getName() + "]."
+ joinPoint.getSignature().getName(),
"Exception occured with arguments : "
+ Arrays.toString(joinPoint.getArgs())
+ ", Time to execute : "
+ (System.currentTimeMillis() - startTime) + "ms",
e);

throw e;
}
}
}

Take note: The runtime exception will not be printed, but checked exception will be printed.

Spring Transaction Rollback


  • transaction rollback testing
1) class in DAO layer


public interface AccountDao {

public void addAccount(Account account)  ;

}


@Repository("accountDao")
public class AccountDaoImpl  implements AccountDao {

@Autowired
        private SessionFactory sessionFactory;

public void addAccount(Account account) {
sessionFactory.getCurrentSession().save(account);

}

}

2) class in service layer


@Transactional(rollbackFor=DBException.class)
public interface AccountService {
public void addAccount(Account account) throws DBException;
}


DBException is a self-defined checked exception.

Only unchecked exceptions (that is, subclasses of java.lang.RuntimeException) are rollbacked by default in spring. If want to roll back for checked exception, need to declare by this way @Transactional(rollbackFor=DBException.class)

@Service("accountService")
public class AccountServiceImpl implements AccountService {

@Autowired
private AccountDao accountDao;

public void addAccount(Account account) throws DBException{
try {
accountDao.addAccount(account);
} catch (Exception e) {
e.printStackTrace();
throw new DBException(DBException.ERR_ADD_ACCOUNTS,
e);
}
}

}

3) junit test



@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:spring-context-service-test.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public class AccountServiceTester {

@Autowired
AccountService accountService;

@Before
public void setUp() throws Exception {
}

@After
public void tearDown() throws Exception {
}


@Test
@Rollback(false)
public void testAccount2() throws DBException{

Account account=new Account();
account.setUserid("0011abc22");
account.setPassword("111");
account.setTimeStamp(new Date());
accountService.addAccount(account);
Account account2=new Account();
account2.setUserid("0011abc22");
account2.setPassword("222");
account2.setTimeStamp(new Date());
accountService.addAccount(account2);
}

@Test
@Rollback(false)
public void testAccount3() throws DBException{

Account account=new Account();
account.setUserid("0011abc4");
account.setPassword("111");
account.setTimeStamp(new Date());
accountService.addAccount(account);

}

@Test
@Rollback(false)
public void testAccount4() throws DBException{

Account account=new Account();
account.setUserid("0011abc4");
account.setPassword("222");
account.setTimeStamp(new Date());
accountService.addAccount(account);

}
}

@Rollback(false) means the data will be committed into database, otherwise spring will rollback all DB operations.

userId is the private key in DB.

Test Result:
a) The data in method testAccount2()  will not be committed to database because the two accountService.addAccount(account) share the same DB transaction and DB will rollback due to duplicate key error
b) The data in method  testAccount3() will be committed to database as it is a standalone DB transaction
c) The data in method  testAccount4() will not be committed to database due to duplicate key error even it is a standalone DB transaction



  • how to use
public void serviceMethod(){

dao.method1();
....
...
// do something
...
...
dao.method2();

}

It is not good to write the source code if dao.method1() and dao.method2() do NOT share the same DB transaction. If writing code as above, the two dao methods will share the same DB transaction.
what we can do is to write a private method to call the two dao methods individually.

Based on the description in http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html


PROPAGATION_REQUIRES_NEW
PROPAGATION_REQUIRES_NEW, in contrast to PROPAGATION_REQUIRED, uses a completely independent transaction for each affected transaction scope. In that case, the underlying physical transactions are different and hence can commit or roll back independently, with an outer transaction not affected by an inner transaction's rollback status.


in the interface, we have the two methods:


public void addAccount2() throws DBException;
public void addAccount3() throws DBException;

in the implementation class,


@Transactional(propagation=Propagation.REQUIRES_NEW, rollbackFor=DBException.class)

public void addAccount2() throws DBException{
Account account=new Account();
account.setUserid("ccc444");
account.setPassword("111");
account.setTimeStamp(new Date());
try{
accountDao.addAccount(account);
}catch(Exception e){
e.printStackTrace();
throw new DBException(DBException.ERR_ADD_ACCOUNTS,
e);
}
}
@Transactional(propagation=Propagation.REQUIRES_NEW, rollbackFor=DBException.class)

public void addAccount3() throws DBException{
Account account=new Account();
account.setUserid("ccc444");
account.setPassword("222");
account.setTimeStamp(new Date());
try{
accountDao.addAccount(account);
}catch(Exception e){
e.printStackTrace();
throw new DBException(DBException.ERR_ADD_ACCOUNTS,
e);
}
}

in the junit class,


@Test
@Rollback(false) public void testAccount5() throws Exception{
     accountService.addAccount2();
     accountService.addAccount3();
 }

then the two methods have their own DB transactions.


Wednesday, February 20, 2013

Add Total Row in jQuery DataTables

1) json response written by spring mvc 3


      @RequestMapping(value = "/getTestData", produces = "application/json")
public @ResponseBody List<TestData> getTestDataTable() {

List<TestData> result = new ArrayList<TestData>();
TestData d1 = new TestData();
d1.setName("Hello A");
d1.setNuma(1100);
d1.setNumb(7);

TestData d2 = new TestData();
d2.setName("B hello");
d2.setNuma(3412);
d2.setNumb(5);
result.add(d1);
result.add(d2);

return result;
}


This is the response sample:

[{"name":"Hello A","numa":1100,"numb":7},{"name":"B hello","numa":3412,"numb":5}]


2) add a table in html


<table id="id_dt_test" class="display">
<thead>
<tr>
<th width="20%">Name</th>
<th width="20%">Number A</th>
<th width="20%">Number B</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<th style="text-align: right">Total:</th>
<th style="text-align: left"></th>
<th style="text-align: left"></th>
</tr>
</tfoot>
</table>

3)  init datatables


$('#id_dt_test').dataTable({
"bProcessing" : true,
"bDestroy" : true,
"sAjaxSource" : 'getTestData.do',
"sAjaxDataProp" : "",
"bFilter" : false,
"bInfo" : false,
"bLengthChange" : false,
"aoColumns" : [ {
"mData" : "name"
}, {
"mData" : "numa",
}, {
"mData" : "numb"
} ],
"bPaginate" : false,
"bAutoWidth" : false,
"fnFooterCallback" : function(nRow, aaData, iStart, iEnd,
aiDisplay) {
var iTotalNuma = 0;
var iTotalNumb = 0;
if (aaData.length > 0) {
for ( var i = 0; i < aaData.length; i++) {
iTotalNuma += aaData[i].numa;
iTotalNumb += aaData[i].numb;
}
}
/*
* render the total row in table footer
*/
var nCells = nRow.getElementsByTagName('th');
nCells[1].innerHTML = iTotalNuma;
nCells[2].innerHTML = iTotalNumb;

}
});



4) actual result:




Reference URL:
http://datatables.net/release-datatables/examples/advanced_init/footer_callback.html

Wednesday, February 6, 2013

css/js files are locked if running in jetty

css/js files are not allowed to edit if running in maven jetty plugin, so it is very troublesome to restart jetty if need to edit css/js files.


  • why
refer to http://docs.codehaus.org/display/JETTY/Files+locked+on+Windows
  • solution
need to change useFileMappedBuffer to false in webdefault.xml


<param-name>useFileMappedBuffer</param-name>
<param-value>true</param-value> <!-- change to false -->

if you are using maven jetty plugin in eclipse, how to find the file webdefault.xml

refer to http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin

for the plugin, the file webdefault.xml in jar file jetty-webapp.jar

so go to %user%\.m2\repository\org\eclipse\jetty\jetty-webapp\{jetty version}\jetty-webapp-{jetty version}.jar

open org/mortbay/jetty/webapp/webdefault.xml and change the configuration mentioned above