Application Support

Der Application Support ist eine Bibliothek mit integrativen Code für JSF2, JSF Ext, Spring Framework und JPA / Hibernate. Ziel ist, häufig verwendete Komponenten für den einfachen Aufbau einer Applikation erfolgreich zu kombinieren. Application Support liefert eine Reihe nützlicher Bausteine. Application Support kann über das Intersult Maven Repository bezogen werden:

<dependency>
    <groupId>com.intersult</groupId>
    <artifactId>application-support</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

Context#

Während in JSF-Applikationen der JSF-Context durch FacesContext.getCurrentInstance() geliefert werden kann, stellt der Application Support den Spring- und damit meist den allgemeinen Bean-Support durch die Klasse com.intersult.application.spring.Context zur Verfügung:

MethodeBeschreibung
getContextLiefert den (Spring-)ApplicationContext
createEntityManagerLiefert den EntityManager zum Durchführen von Datenbank-Operationen außerhalb der Spring-Injection, wie zum Beispiel beim Bau von Validators und Converters. Der so erhaltene EntityManager muss in jedem Fall in einem try-finally-Block mit der Methode close geschlossen werden. Andernfalls entsteht ein Speicher- und Ressourcen-Leck.

Bean-Validators#

Die Annotations direkt an Java-Bean-Properties stellen sicher, dass ein Wert nur in einem bestimmten Zustand in die Datenbank geschrieben wird. Bean-Validation in JSF2 validiert die Annotations bereits im Frontend und bietet die Möglichkeit einen Fehler unmittelbar dem Benutzer zu melden. Die Annotations können auch in anderen Beans verwendet werden, die nicht in die Datenbank geschrieben werden. Die saubere Konfiguration wird dabei durch JSF Ext hergestellt.

Spring Custom Injections#

Der Application-Support stellt das Interface InjectionFactory<A extends Annotation> zur Verfügung. Damit können auf sehr einfache Weise eigene Injection-Anntotations erstellt werden.

Beispiel Scope-Injection#

Generell ist es in Spring möglich, durch die @Value-Annotation beliebige Werte durch EL-Expression zu injecten, zum Beispiel durch #{scope.field} einen Wert aus einem JSF Ext Scope. Die Nachteile bestehen in der jeweiligen Angabe der EL-Expression, obwohl der Feldname bereits als Information ausreichend wäre, sowie dass der Wert nicht Null sein darf.

Daher eine ScopeInjectionFactory:

@Component
public class ScopeInjectionFactory implements InjectionFactory<ScopeValue> {
	@Override
	public Class<ScopeValue> getAnnotationType() {
		return ScopeValue.class;
	}
	@Override
	public Object create(Class<?> type, String beanName, ScopeValue annotation) {
		Scopes scopes = Scopes.instance();
		if (scopes == null)
			return null;
		Scope scope = scopes.getScope();
		if (scope == null)
			return null;
		return scope.get(beanName);
	}
}

Die dazugehörige Annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface ScopeValue {
}

Die Spring-Injection der JSF Ext Scopes ist auch in der Jsf Spring Integration enthalten.

Factory Methods#

Eine andere Möglichkeit zur Injection sind Factory Methods. Diese bedienen die standardmäßige Annotation @Autowired, im Unterschied zu Injection Factories. Der Design-Zweck liegt im Generieren spezieller Beans, die zum Beispiel von der Basisbean abgeleitet sein können. Durch eine Factory generierte Beans brauchen auch keine Spring-Annotation besitzen, sie können sogar während der Laufzeit durch einen Bytecode-Generator erzeugt werden.

Ein einfaches Beispiel kann so aussehen:

@Component
public class AutowireTest {
	@Autowired
	private InjectBean injectBean;
	
	@Factory
	public InjectBean injectBeanFactory() {
		return new InjectBean("from factory");
	}
	
	@Test
	public void test() {
		System.out.println(injectBean.getText());
	}
}

public class InjectBean {
	private String text;
	
	public InjectBean(String text) {
		this.text = text;
	}

	public String getText() {
		return text;
	}
}

Erklärung: In diesem Fall ist InjectBean ein einfaches POJO. Die Factory und die Verwendung liegen sogar innerhalb der gleichen Component, natürlich kann die Factory auch durch jede andere Spring-Component implementiert werden.

Session Events#

In einem Servlet treten neben dem HttpServletRequest auch Events vom Typ HttpSessionEvent auf, wie zum Beispiel bei einem HttpSessionListener. Möchte man darin Spring-Operationen durchführen, braucht man einen ordentlich initialisierten Spring-Context inklusive den RequestAttributes.

Die Klasse EventRequestAttributes stellt diese zur Verfügung:

WebApplicationContext applicationContext =
	WebApplicationContextUtils.getRequiredWebApplicationContext(event.getSession().getServletContext());
EventRequestAttributes request = new EventRequestAttributes(event);
try {
	RequestContextHolder.setRequestAttributes(request);
} finally {
	request.requestCompleted();
	RequestContextHolder.resetRequestAttributes();
}

Die Klasse ContextSessionListener implementiert dies bereits für den HttpSessionListener, sodass sie bequem abgeleitet werden kann:

public class UserListener extends ContextSessionListener {
	@Override
	public void sessionCreated(HttpSessionEvent event, WebApplicationContext applicationContext) {
		Authenticator authenticator = applicationContext.getBean(Authenticator.class);
		authenticator.createSession(event.getSession());
	}
	@Override
	public void sessionDestroyed(HttpSessionEvent event, WebApplicationContext applicationContext) {
		Authenticator authenticator = applicationContext.getBean(Authenticator.class);
		if (authenticator.getCurrentUser() != null)
			authenticator.logout(event.getSession());
	}
}