Thursday, January 12, 2012

Migrate to Hibernate 4 and Spring 3.1 - SessionFactory

As most of you probably have to do these days or in the near future we have to migrate our project to the newest Versions ob Hibernate and Spring. Actually the Version numbers are
  • Hibernate 4.0.0.Final
  • Hibernate Search 4.0.0.Final
  • Spring 3.1.0.RELEASE
If you want to perform a complete migration you'll need to change also to Spring 3.1. because in that version they added support for hibernate 4. For example they added the package
org.springframework.orm.hibernate4
which is needed for example to create the org.springframework.orm.hibernate4.LocalSessionFactoryBean which defines all the annotated classes and the hibernate properties needed to create the session

<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" p:dataSource-ref="dataSource">
<property name="annotatedClasses">
<list>
<value>org.something.myClass</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.connection.driver_class=${hibernate.connection.driver_class}
hibernate.connection.url=${hibernate.connection.url}
hibernate.connection.username=${hibernate.connection.username}
hibernate.connection.password=${hibernate.connection.password}
hibernate.default_schema=${hibernate.default_schema}
hibernate.hbm2ddl.auto=${hibernate.hbm2ddl.auto}
hibernate.jdbc.batch_size=${hibernate.jdbc.batch_size}
hibernate.c3p0.max_size=${hibernate.c3p0.max_size}
hibernate.c3p0.min_size=${hibernate.c3p0.min_size}
hibernate.c3p0.timeout=${hibernate.c3p0.timeout}
hibernate.c3p0.max_statements=${hibernate.c3p0.max_statements}
hibernate.c3p0.idle_test_period=${hibernate.c3p0.idle_test_period}
hibernate.c3p0.acquire_increment=${hibernate.c3p0.acquire_increment}
hibernate.c3p0.validate=${hibernate.c3p0.validate}
hibernate.cache.region.factory_class=${hibernate.cache.region.factory_class}
hibernate.connection.provider_class=${hibernate.connection.provider_class}
hibernate.show_sql=${hibernate.show_sql}
hibernate.generate_statistics=${hibernate.generate_statistics}
hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=true
</value>
</property>
</bean>
As you can see there is no definition of  eventlisteners whatsoever. Before version 3.6 of hibernate-search the changelisteners where defined here, which is now obsolete.

<!-- Obsolete in 3.6: FullTextIndexEventListener default constructor is obsolete. Remove all explicitevent listener configuration. As of Hibernate Core 3.6 Hibernate Search will be automatically enabled if it is detectedon the classpath.
<property name="eventListeners">  
<map>
<entry key="post-update">
<bean class="org.hibernate.search.event.FullTextIndexEventListener" />
</entry>
<entry key="post-insert">
<bean class="org.hibernate.search.event.FullTextIndexEventListener" />
</entry>
<entry key="post-delete">
<bean class="org.hibernate.search.event.FullTextIndexEventListener" />
</entry>
</map>
</property>-->
Another interesting change is that the class definition not just changed from hibernate3 to hibernate4 but in our case from org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean into org.springframework.orm.hibernate4.LocalSessionFactoryBean.
This leads to the lost of the possibility of defining a lobHandler which according to the JavaDoc of the LobHandler interface is still a good practice. I asked about this at stackoverflow, but I didn't got an answer yet, so I asked it today on the spring forum.


Furthermore you notice that now the complete connection properties including the c3p0 (cache) definition is defined in this bean. This makes it much simpler because there is no additional "datastore" bean needs to be there.
However there is still a "datastore" bean which needs to be defined otherwise you'll get a NullPointerException. (UPDATE: This will apparently be fixed in Spring 3.1.1)


Another important thing that needs to be defined in this bean is the hibernate-search. It's properties changed slightly in version 4.0 and the most important ones are shown below.
hibernate.search.lucene_version=${hibernate.search.lucene_version}
hibernate.search.analyzer=${hibernate.search.analyzer}
hibernate.search.default.directory_provider=${hibernate.search.default.directory_provider}
hibernate.search.default.filesystem_access_type=${hibernate.search.default.filesystem_access_type}
hibernate.search.default.locking_strategy=${hibernate.search.default.locking_strategy}
hibernate.search.default.indexBase=${hibernate.search.default.indexBase}
hibernate.search.default.indexmanager=${hibernate.search.default.indexmanager}
hibernate.search.default.refresh=${hibernate.search.default.refresh}
hibernate.search.default.reader.strategy=${hibernate.search.default.reader.strategy}
The real values of the properties depend highly on your environment, however if you need my values just drop me a comment and I'll add them.

UPDATE:
Unfortunately there is still an Error in hibernate 4.0.0.Final regarding the progress monitor when re-indexing the whole database. It seems that this error was found in CR1then fixed in CR2 and reappeared in the final version

Wednesday, January 4, 2012

Apple TV Untethered Jailbreak iOS5 (4.4.4)

Finally it is possible to update the Apple TVs to the newest version. Of course I know that it was possible since  Apple released it, however in my case the un-jailbreaked Apple TV is not really useful.

The problem is that I'm living in a region close to the border of my country and the official state language is not my mother tongue. Unfortunately you can not borrow multilingual movies in the iTunes store, so already the main functionality of the ATV is quite useless for me.

Despite that I don't care about that particular feature the ATV is really great, and if you jailbreak it, it is even better. Airplay works incomparably well between iDevices and sharing your iTunes libraries is perfekt for data that is used very often such as the music on your disc. If you want more I'll suggest to install nitoTV, XBMC and IceFilms.

Jailbreak:
What can I say to jailbreaking... At the end it is really straight forward and described best on the firecore webpage where you can also download the jailbreak-program.

What you need is:


Now just update your ATV to the latest Version using iTunes(to test if everything works well with the original Version) and jailbreak it using the instruction on the  firecore webpage.

Now we need to avoid that ATV updates its OS which would mean that all changes are lost we need to change the /etc/hosts file as follows; First backup the original file
cp /etc/hosts /etc/hosts.bak
Redirect the update sites to localhost, which means that there is no chance to ever find an update
echo "127.0.0.1 appldnld.apple.com" >> /etc/hosts
echo "127.0.0.1 mesu.apple.com" >> /etc/hosts
echo "127.0.0.1 appldnld.apple.com.edgesuite.net" >> /etc/hosts
This changes can be removed as follows (if you need to) or use the backup
sed -i '/127.0.0.1 appldnld.apple.com/d' /etc/hosts
sed -i '/127.0.0.1 mesu.apple.com/d' /etc/hosts
sed -i '/127.0.0.1 appldnld.apple.com.edgesuite.net/d' /etc/hosts
UPDATE: In the actual jailbreak the /etc/hosts is changed already by the jailbreak

Install nitoTV:
On Mac its simple, just open a terminal whereas on windows you'll need to download PuTTY. Now just connect to your ATV via openSSL.

  1. SSH into your ATV (e.g.ssh root@atv.ip  with default password “alpine”)
  2. passwd” to enter a new password (if you want to)
  3. echo “deb http://apt.awkwardtv.org ./” > /etc/apt/sources.list.d/awkwardtv.list
  4. apt-get update
  5. apt-get install com.nito.nitoTV
  6. killall Lowtide

Now after the restart you should have nitoTV installed and see it on the main ATV page.


Install XBMC:
That's even simpler; Within nitoTV there is one package in the list called XBMC-ATV2. Just istall that one and your done!


Install IceFilms:
IceFilms is a nice AddOn for XBMC that needs to be installed via SFTP.
1) Download the file (make sure that it remains .zip)
Then you'll need a program to send files to the ATV which for Windows can be WinSCP. Despite that the MAC is SSL-friendly there exists a simple GUI program for doing this which is CyberDuck
2) Sent the file via SFTP (default port 22) to the ATV in folder /private/var/mobile/Media folder
3) Open XBMC
4) Go to System --> AddOns
5) "Install from ZIP file" and go to the folder where you put the Zip and install it


Now the AddOn is installed and you can find it in the list of the Videos --> AddOns


Now that you have everything you need just enjoy your ATV2+ :-D

Friday, December 23, 2011

CAS Domain Solution

It's been a while now that we where stuck on this Problem. Basically it is like this, that we can not be sure in our situation that all our applications are able to distinguish between to identical usernames in different domains.

We know that it is against the principle of CAS to move some parts of the authorization from the application to the SSO, however it is needed in our case, and at the End it was not that difficult. Basically we have added a new database table which holds the allowed domains for each service. So we are able to check during the validation step if the service requested accepts the entered credentials.

The support for domains is similar to the one for attributes, so it was enough to just extend the RegisteredService interface adding methods for getting the list of allowed domains and a boolean that defines if the domain selection should be considered.

public interface DomainRegisteredService extends RegisteredService {
/**
    * Sets whether we should bother to read the domain list or not.
    *
    * @return true if we should read it, false otherwise.
    */
   boolean isIgnoreDomains();
   /**
    * Returns the list of allowed domains.
    *
    * @return the list of domains
    */
   List getAllowedDomains();
}

Unfortunately you cannot just extend the original implementation, so we had to copy it and extend it with the needed fields and methods.

@ElementCollection(targetClass = String.class, fetch = FetchType.EAGER)
@JoinTable(name = "drs_domains")
@Column(name = "d_name", nullable = false)
@IndexColumn(name = "d_id")
private List allowedDomains = new ArrayList();
private boolean ignoreDomains = true;

This change introduces the need of extending or exchanging some classes like the ServiceRegistryDao and the RegisteredServiceValidator. However this changes are quite straight forward, so I'll not going to post them right now. The more important change is in the CentralAuthenticationServiceImpl where we need to check the domains.

@Override
@Audit(action = "SERVICE_TICKET_VALIDATE", actionResolverName = "VALIDATE_SERVICE_TICKET_RESOLVER", resourceResolverName = "VALIDATE_SERVICE_TICKET_RESOURCE_RESOLVER")
@Profiled(tag = "VALIDATE_SERVICE_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public Assertion validateServiceTicket(final String serviceTicketId, final Service service) throws TicketException {
[..]
if (!registeredService.isIgnoreDomains()) {
boolean domainValidated = false;
if (principal == null || principal.getAttributes() == null) {
log.error("Domain of ServiceTicket [" + serviceTicketId + "] with service [" + serviceTicket.getService().getId() + " can not be determined");
throw new TicketValidationException(serviceTicket.getService());
}
List<Pair<String, String>> attributes = getAttributesOfPrincipal(principal.getAttributes().get("distinguishedName"));
List<String> domains = registeredService.getAllowedDomains();
if (domains != null && attributes != null) {
for (Pair<String, String> attribute : attributes) {
if (attribute.getLeft().equals("DC") && domains.contains(attribute.getRight())) {
domainValidated = true;
}
}
}
if (!domainValidated) {
log.error("Domain of ServiceTicket [" + serviceTicketId + "] with service [" + serviceTicket.getService().getId() + " not allowed to use CAS with this domain");
throw new TicketValidationException(serviceTicket.getService());
}
}

What's still missing is how to populate the list of domains, however that's also done like the one for attributes in the deployerConfigContext.xml.

Thursday, December 1, 2011

Hibernate Statistics & Criteria Queries

Recently we had to check the performance of our systems and a main part of that is beside the UI the database performance. For that purpose we came over Hibernate Statistics (of course because we access our DB over Hibernate)

As we are using Spring to configure our back end we had to define the statistics bean using JMX because it fits better in our project. It can be done using the following definition
<bean id="jmxExporter"
    class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
        <map>
            <entry key="Hibernate:name=statistics" >
                <ref local="databaseStatisticsService" />
            </entry>
        </map>
    </property>
</bean>
<bean id="databaseStatisticsService"
    class="it.mydomain.core.service.impl.DatabaseStatisticsServiceImpl">
    <property name="statisticsEnabled" value="${hibernate.generate_statistics}" />
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>
The property "${hibernate.generate_statistics}" contains just "true" and needs to be added also to the hibernate properties in the format "hibernate.generate_statistics=${hibernate.generate_statistics}"

The "databaseStatisticsService" is just a simple extension of the "org.hibernate.jmx.StatisticsService" providing some simple methods to retrieve the statistics in a convenient way. It's interface its like the following

import org.hibernate.jmx.StatisticsServiceMBean;
import org.hibernate.stat.CollectionStatistics;
import org.hibernate.stat.EntityStatistics;
import org.hibernate.stat.QueryStatistics;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
public interface DatabaseStatisticsService extends StatisticsServiceMBean {
boolean isDbStatisticsEnabled();
Statistics getDbStatistics();
List getDbStatisticSummary();
List getDbStatisticCollections();
List getDbStatisticEntities();
List getDbStatisticQueries();
List getDbStatisticSecondLevelCache();
}

In this interface you can already see, that there is a method "List getDbStatisticQueries();" which should contain the data for each performed query; at least we thought that it should be like that :-)

However by default it contains just the data for the HQL queries but not the ones for the Criteria ones. After a short Google excursion we found that there is an issue that is still open in the hibernate bug tracker. Usually in my experience efficient database queries are a main factor of the applications performance, so we had to find a way to add this data, which turned out to be much simpler as expected.

The main idea is just to add a simple method to the "org.hibernate.loader.criteria.CriteriaLoader" class that allows the Statistic collector to index also that data. The simplest way to do so is just to add the method using an aspect like the following
package it.unibz.ict.core.util.aspect;

import org.hibernate.loader.criteria.CriteriaLoader;
import org.hibernate.stat.Statistics;

/**
 * Aspect adding a method to {@link CriteriaLoader} to allow Hibernate
 * {@link Statistics} pick up criteria queries, too.
 */
public privileged aspect CriteriaStatisticsAspect {
public static final String PREFIX = "[CRITERIA] ";
/**
* @see http://opensource.atlassian.com/projects/hibernate/browse/HHH-3452
* @return
*/
public String CriteriaLoader.getQueryIdentifier() {
return PREFIX + getSQLString();
}
/**
* Dummy method; without this Eclipse would see an error in the getSQLString
* call above
*
* @return dummy value
*/
private String getSQLString() {
return null;
}
}
Problem solved! At least until this method is added by default by the hibernate developer team.


Tuesday, November 29, 2011

CAS Domain Problem

In our company we have the problem, that we have 2 different domains where the authentication can happen. Unfortunately we can not assure that the credentials are unique over both Domains. e.g. there could be a user "Tom" in Domain1 and another "Tom" in Domain2 which belong to two different physical users (hopefully with two different passwords)

Now where's the problem? Well in the various applications we need to authorize the user depending on their position in the company. So it could be that the "Tom" in Domain1 can access an application but the "Tom" from Domain2 not. So how we can distinguish between the two "Toms"?

Despite that some of the applications, basically the ones that are developed in-house, can be extended to consider the domain passed as argument(see CAS & Attributes), there are some third party apps that cannot. So how those applications could have worked before? Basically for those we have one running version per domain, which solved the problem.


For those now we had to find another way. As we cannot check it on the application side the domain check needs to be implemented on CAS meaning that on "validateServiceTicket" another check to the authenticating Domain needs to be placed.

Important in this case is not to change to much of the original structure of CAS, in order not to loose the possibility to update when CAS updates. The solution has not yet been implemented nor tested, so stay tuned for the solution post.

UPDATE: Finally there is the solution

Friday, November 25, 2011

CAS Themes and Views

Recently we faced a big problem in our organisation regarding the look and structure of the login pages which could not be solved by CAS standard theme concept. By default the theme definition of CAS just changes the default CSS which for sure offers a wide variety within the pages, but made it difficult for us to fit our needs.

--> We had to change it in a way that also the views change

1) In the cas properties instead of telling the real name of the views I want to see just direct in any case to some defualt view like this cas.viewResolver.basename=views-default

2) Under src/main/webapp/WEB-INF/classes add one property file for each theme you want to use afterwards
src/main/webapp/WEB-INF/classes
|
+- cas-theme-theme1.properties
|
+- cas-theme-theme2.properties
|
+- ...

3) In this files just add one more property (beside the ones that are already there by default)
theme.view=/WEB-INF/view/jsp/theme1/ui/
When the structure of your views is still like the one from default it should point perfektly to the views of theme1 in this example


4) Now change the default views e.g. src/main/webapp/WEB-INF/view/jsp/default/ui/casLoginView.jsp to the following

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:theme code="theme.view" var="theme"></spring:theme>
<jsp:include page="${theme}casLoginView.jsp"/>

With this definition now the casLoginView.uses the view defined by the theme.view property in cas-theme-themeX.properties. file.


And now how to change the property file? Just use the Services GUI (e.g. https://login.cas.net/cas/services/manage.html) and enter there the name of the theme for the various services. And that's actually the power of the solution. Now you can define one default theme for your server and a lot of "real" themes for the various services that use you're CAS.


Of course it is a change in the basic logic of CAS, however it is reversable in a few steps if one day changing the basic CSS would be enough


UPDATE:
By default the theme names are cas-theme-xxx which is at least 10 chars long. The default implementation of  the CAS Serive Managemant GUI allows theme names with a maximum length of 10, so change the add.jsp to allow the longer names

Linux boots into memtest86

Today I had the problem, that my ubuntu instance was booting automatically into the memtest86 grub entry. This was mainly because that was the only grub entry that remained after a linux image update.

The simplest way to fix this problem was using a live CD and the following commands:
1) boot into live CD

2) create a directory to mount the old linux on your hdd
sudo mkdir /mnt/temp
3) mount it
sudo mount /dev/sda1 /mnt/tempfor i in /dev /dev/pts /proc /sys; do sudo mount -B $i /mnt/temp$i; done
4) copy the resolve.conf into the original ubuntu so that the apt-get will work in future commands
sudo cp /etc/resolv.conf /mnt/temp/etc/resolv.conf
5) change the root directory to the newly created mount point
sudo chroot /mnt/temp
6) now check if the kernel is already on your system. If not just install it
apt-get install linux
6.1) do some cleaning in your system
apt-get autoclean
apt-get clean
apt-get update
apt-get upgrade
apt-get install -f
dpkg --configure -a
7) if this still not works try to reinstall grub
apt-get purge grub grub-pc grub-common
apt-get install grub-common grub-pc
update-grub
8) umount the whole stuff
for i in /dev/pts /dev /proc /sys / ; do sudo umount /mnt/temp$i ; done
9) restart the system

Now it should boot into the actual linux kernel and your system is back!

Golang setup PATH

Quite recently we startet in the company to use and write some Go programs. I love Go. It's easy to learn, read and modify. One of the m...