-
Envers 1.2.3-hibernate-3.3 – backport release
Posted on August 21st, 2010 No commentsHernan Chanfreau backported some of the recent Envers changes and fixes from the 3.5 branch. The detailed list of the backported issues can be found in this forum post.
I applied the patch and released a new Hibernate-3.3 compatible Envers version, it’s available in the JBoss Maven repository.
Thanks a lot to Hernan for the work and providing the patch! :)
Adam
-
New features in cdi-ext
Posted on August 2nd, 2010 No commentsTomek Szymański just commited two new features to cdi-ext.
The first is the ability to secure JSF pages when using the Nav component to handle navigation (more on it here). If you want to make a page accessible only if a certain EL expression is true, when defining a page, you can write:
private final Page adminPage = new ViewIdPageBuilder("/admin.xhtml") .setRequiresLogin(true) .setSecurityEL("#{currentUser.isAdmin)").b();If the condition is not met, the user will get 403 Forbidden. Also, Tomek extended handling of
setRequiresLogin(true), so that if a user is redirected to the login page, because he was not logged in and the page is secured, the page which the user tried to access is stored and after logging in, he is redirected back to the original page.The second is the ability to specify the transaction timeout using a
@TransactionTimeout(timeout = 5)annotation on a method.Moreover, we have moved our repository infrastructure to Nexus. The new address where cdi-ext artifacts are deployed is http://tools.softwaremill.pl/nexus/content/repositories/snapshots.
Adam
-
Ruby on Rails + CDI? Why not! Enter TorqueBox + Weld
Posted on July 27th, 2010 1 commentI guess many people are often “unsatisfied” with how JSF works and how much time it sometimes takes to do a simple thing. That’s why we are trying out a new combination: RoR for the frontend and CDI for the backend. How?
Deploying RoR applications to JBoss AS is really easy thanks to the TorqueBox project. You just deploy a
.ymlfile using the providedraketasks and you can develop the application “live” – no redeploys, instant changes, and so on.Using RoR as a frontend to a CDI/Weld based application requries two more steps, so that RoR can see the business logic classes and share the same http session with CDI (so it’s possible to access
@SessionScopedbeans from RoR and CDI code).First you need to deploy your application in the
DefaultDomain(at least until TORQUE-85 is fixed). To do this, add ajboss-classloading.xmlfile to theMETA-INFdirectory with this content:<classloading xmlns="urn:jboss:classloading:1.0" domain="DefaultDomain" top-level-classloader="true" export-all="NON_EMPTY" import-all="true"> </classloading>Secondly, you need to add a filter to RoR’s web application, so that Weld and RoR share the same session. Just edit
config/web.xmlin your RoR application (the magic in the RoR deployer will add it to the virtual .war deployment it creates) and add the following:<web-app> <listener> <listener-class>org.jboss.weld.servlet.WeldListener</listener-class> </listener> </web-app>Now RoR and CDI share the same session (so you can use
@SessionScopedbeans etc, probably also@ConversationScoped, but I haven’t tried that). You can lookup CDI beans from RoR code using e.g. the BeanInject class from cdi-ext, or just by writing a very simple utility method which lookups theBeanManager.Adam
-
Initial valid-time support in Envers
Posted on July 2nd, 2010 No commentsI just commited initial support for valid-time auditing in Envers, a feature that a lot of users has been (directly or indirectly) asking for. It’s joint work, as Stephanie Pau contributed a patch with a large portion of those changes – thanks!
You can try it out by checking out Hibernate trunk source code.
What is valid-time about? So far Envers only stored the revision at which a change was made. This information is enough to retrieve historical data, however the queries are quite complicated and in advanced use cases can be time-consuming. This can be improved when we store both the start and end revisions, that is information on when data was “valid”. For historic entities, both column are filled, and for “current” data, the end revision column is
null.Using the valid-time audit strategy, it will be possible to:
- speed up and simplify the queries to retrieve historical data
- implement support for queries, which traverse relations
- implement other types of queries, like latest changes
To configure Envers to store the end-revision number, you have to specify a property in your configuration file:
<property name="org.hibernate.envers.audit_strategy"> org.hibernate.envers.strategy.ValidTimeAuditStrategy </property>
Envers will then generate and additional
REVENDcolumn (next to theREVcolumn) in every audit (_AUD) entity/table; however this column won’t be part of the primary key. You can change the name of the end-revision column by setting theorg.hibernate.envers.audit_strategy_valid_time_end_nameproperty value.The value of the end-revision column can be calculated basing only on the original revision-changed columns, so using a couple of queries it will be possible to easily migrate existing data to the new audit strategy.
Please note that this feature is experimental, and can be changed in the future. The associated JIRA issue is HHH-3763.
Adam
-
NEnvers
Posted on July 1st, 2010 No commentsIf you are a .NET and NHibernate user, soon you’ll be able to use Envers in your project!
Simon Duduica has been working on an Envers to .NET port. This is still work in progress, but you can check out the current source code here: https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib/trunk/sandbox/simondud/Envers.NET
A large portion was ported, but there’s also quite a lot left, so any help is appreciated. Big thanks to Simon and looking forward to a release :)
Adam
-
Object Services in Scala
Posted on June 21st, 2010 2 commentsUsing Scala’s implicits it’s possible to implement Object Services in a much more “user-friendly” way. Just to remind, the goal is to extend a class hierarchy with a method, polymorphically. E.g. we have:
trait Animal class Elephant extends Animal class Ant extends Animal
and we want to add a
paintmethod, which has a different implementation for an elephant, and for an ant (quite obviously :) ). In another words, we want to have a polymorphic extension method. One approach in Scala is to use pattern-matching. And in fact pattern-matching can be used to implement object services as described here (below is a method which uses reflection, but it can be easily swapped).Before we’ll go to the actual implementation, here’s how you can use the object services. We already have the objects, so it’s time to create the services:
trait PaintService[O <: Animal] extends OS[O] { def paint(c: Canvas): Unit } class ElephantPaintService(elephant: Elephant) extends PaintService[Elephant] { def paint(c: Canvas): Unit { ... } } class AntPaintService(ant: Ant) extends PaintService[Ant] { def paint(c: Canvas): Unit { ... } }Now we need a place, where we register the services. In the Weld implementation, this was done automatically at container startup time. Here we’ll need one object (which could be auto-generated by a compiler plugin):
object PaintServiceReg extends OSP[Animal, PaintService] { register[Elephant, ElephantPaintService] register[Ant, AntPaintService] }The type bound’s of the
registermethod will make sure that you can register only appropriate services for appropriate object types. Finally, we can use our services. If we want to use thePaintService, we’ll need to import the content of thePaintServiceRegobject. That way we have control, on what services are available when. Usage is quite simple and looks like a regular method invocation, as if the method was in the class hierarchy:import PaintServiceReg._ val animal1: Animal = new Elephant animal1.paint(c) // ElephantPaintService is called val animal2: Animal = new Ant animal2.paint(c) // AntPaintService is called
What’s left to show is the actual code for
OSandOSP:trait OS[O] trait OSP[O <: AnyRef, S[_ <: O] <: OS[_]] { implicit def oToS(obj: O): S[O] = { val bestService = findBestService(obj.getClass()) bestService.getConstructors()(0).newInstance(obj).asInstanceOf[S[O]] } protected def register[RO <: O, RS <: S[RO]](implicit manifestRO: Manifest[RO], manifestRS: Manifest[RS]) { serviceMap += manifestRO.erasure -> manifestRS.erasure } private var serviceMap: Map[Class[_], Class[_]] = Map() private def findBestService(objectCls: Class[_]): Class[_] = { serviceMap.foldLeft[(Class[_], Class[_])]((classOf[AnyRef], classOf[AnyRef])) ((curr, mapping) => { // Checking if the mapping is appropriate for the given object class // and more specific than the current one if (mapping._1.isAssignableFrom(objectCls) && curr._1.isAssignableFrom(mapping._1)) mapping else curr })._2 } }The implementation can be improved e.g. to always return the same object service reference for a given object (something like Eclipse Adapters – thanks for the link), or to provide alternative ways to instantiate the services – not necessarily with a one-arg constructor.
The biggest problem here is that completeness is not checked – that is, if there’s a service missing for a class in the hierarchy, there will be a run-time error. But I think this also can be checked with a compiler plugin.
-
Object Services, or bridging anemic and rich models, in CDI/Weld
Posted on May 27th, 2010 3 commentsRich domain models are certainly a nice, object-oriented idea, but I always had one problem with them: what if they become bloated with completely unrelated methods? For objects that are frequently used in a system, we may want to add various methods, which depend on the actual class of the object. Also, what if we’d like to use some other (e.g. CDI) beans as part of the method logic? Normally in DI frameworks there’s no injection into model classes. Or we want to add a frontend-specific method, but we receive the instances from a backend service?
Object Services try to address the issues above. Suppose we have a simple class hierarchy of animals:
abstract class Animal class Elephant extends Animal class Ant extends Animal
and we want to implement a
paintmethod, which paints a picture of the given animal on a canvas. Quite obviously, painting an elephant is different from painting an ant. There are several solutions:- add a paint method to the
Animalinterface – problems outlined above - add a paint method, in which we check which
Animalwas passed usinginstanceof– quite ugly - use the visitor pattern – typesafe, but quite verbose
I think the best solution would be to have type-safe “polymorphic extension methods”, so that in your code you could just add some methods to each class in a hierarchy, but unfortunately this isn’t supported by any Java (see also multiple dispatch).
Another possibility is to use what I call “Object Services”. If we want to add some methods to a class hierarchy, we create a parallel hierarchy of “services” (which are normal classes):
interface PaintService<T extends Animal> extends OS<T> { void paint(Canvas c); } // Implements OS<Elephant> class ElephantPaintService implements PaintService<Elephant> { // Here we can store the object, for which the service was invoked void setServiced(Elephant e) { ... } void paint(Canvas c) { ... } } // Implements OS<Ant> class AntPaintService implements PaintService<Ant> { // Injection works normally @Inject AnthillService anthill; void setServiced(Ant a) { ... } void paint(Canvas c) { ... } }OSis an interface marking some beans as object services; the class, to which the service corresponds is given as a type parameter.The
ObjectServiceExtensionwill detect all beans that implement theOSinterface, and register anOSP(Object Service Provider) bean which can be later injected to obtain a correct object service given anAnimal:@Inject OSP<Animal, PaintService<Animal>> paintService; void paint(Animal a, Canvas c) { paintService.f(a).paint(c); }Each invocation of the
fmethod will lookup the correct bean, based on the run-time type of the object passed, create a new instance of the found bean and set the object, for which the method was called. All beans created are CDI-managed, so injection etc works normally.void test(Canvas c) { // Will invoke paint(c) in AntPaintService paint(new Ant(), c); // Will invoke paint(c) in ElephantPaintService paint(new Elephant(), c); }The source code is available on GitHub in the cdiext project. To use it, just bundle the jar with your application.
Thanks to Tomek Szymański for discussing the implementation.
So what’s next? The code could use a couple of improvements, but the biggest next task is to add deploy-time checking if there’s an object service for each class in a hierarchy (e.g. we have a
PrintServiceand anElephantPrintService, but forget to add anAntPrintService).Adam
- add a paint method to the
-
CDI & Weld Extensions in Git
Posted on May 17th, 2010 No commentsHello,
I’ve created a new
cdiextproject at github, initially with two extensions:1. Stackable Security Interceptors, about which I blogged here and here. Example usage:
@SecureBinding @Secure("#{loggedInUser.administrator}") public @interface AdministratorOnly { } public class SecureBean { @AdministratorOnly @Secure("#{additionalSecurityCheck}") public void doSecret() { ... } }2. Injectable ELEvaluator, which works both during a faces request and outside of one (e.g. during invocation of an MDB). Example usage:
@Inject private ELEvaluator elEvaluator; void someMethod() { // ... Integer result = elEvaluator.evaluate( "#{testParam1 + 10 + testParam2}", Integer.class, params); // ... }Thanks to Dan Allen for helping out with this one.
There are also some tests done using Arquillian – looks like it’s going to be a great testing tool! :)
Adam
-
JSR-308, checkers framework and static analysis on GeeCON
Posted on April 26th, 2010 1 commentI’ll be speaking about the JSR-308 specification (annotations on java types), which will be part of Java 7, on the GeeCON conference, which will take place from the 13th till the 14th of May 2010 in Poznan, Poland.
Apart from an introduction to the new annotations, I will cover the checkers framework, and do a live demo of some of the bundled checkers (nullability, immutability), as well as of my typestate checker. I will also show how to implement a simple custom checker, using the framework.
If you’ll be there, be sure not to miss it. If you’ve not yet registered, visit http://2010.geecon.org :).
See you there,
Adam -
Extending the security interceptor for Weld/JSF2
Posted on April 13th, 2010 2 commentsIn my previous post, I described how to create a simple security interceptor, which checks conditions defined using EL expressions, e.g.:
@Secure("#{loggedInUser.name == arg0.name}") public List<Message> listMessages(User owner) { ... }Now, it would be nice to be able to stack such annotations, so that they can be placed:
* on methods
* on classes – then the constraint applies to all methods
* on other annotations, to create “security bindings”An example usage could be:
@Secure("#{loggedInUser != null}") public class Messages { @AdminOnly public void deleteAllMessages() { ... } @Secure("#{loggedInUser.maxMessageListCount == count}") public List<Message> listMessages(@ELVar("user") User owner, @ELVar("count") int count) { ... } }where
@AdminOnlyis defined as:@SecureBinding @Secure("#{loggedInUser.isAdministrator}") public @interface AdminOnly { }This way common security constraints can be expressed as annotations or on the class. I’m also using an improvement suggested by Dan Allen, to name the method arguments using
@ELVar, instead of naming themarg0,arg1, etc. This also allows to refer to method arguments in the “security binding” annotations, whatever the position of the argument is.How to implement such annotations? Well, the first step is to create a portable extension (meaning it will work with any CDI implementation, not just Weld), which will gather, for each method, all the
@Secureannotations, and their values, into one single annotation,@InterceptSecure. Having such a set of constraints to check for each method, we add the annotation to the method meta-data, using a utility class from Weld Extensions: theNewAnnotatedTypeBuilder. As the@InterceptSecureis an interceptor binding, the security interceptor will be called whenever the method is invoked.The extension observes the
ProcessAnnotatedTypeevent, which is fired for each bean type. If necessary, the type can be modified, to include the new annotations. The annotation is only added to Weld meta-data, not to the method itself (so there’s no bytecode manipulation or such).One last obstacle to overcome is to get the value of the generated
@InterceptSecureannotation in the interceptor. Currently this is not possible using e.g.BeanManager, but should be address in CDI Maintenance Release (see here), so as a temporary solution all the generated annotations are stored in a map in the extension. All extensions are application-scoped beans, so the information can be accessed from the interceptor. One shortcoming of the solution is that one method may belong to several, differently annotated CDI beans.The code for the annotation and extension:
@InterceptorBinding public @interface InterceptSecure { @Nonbinding String[] value(); } public class SecurityExtension implements Extension { private final Map<Method, InterceptSecure> interceptSecureForMethods = new HashMap<Method, InterceptSecure>(); public InterceptSecure getInterceptSecure(Method m) { return interceptSecureForMethods.get(m); } public <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> event) { // A flag indicating if the builder was used to modify the annotations boolean used = false; NewAnnotatedTypeBuilder<T> builder = new NewAnnotatedTypeBuilder<T>(event.getAnnotatedType()); // We need to read the values of the @Secure annotation that are present on: // 1. types (classes) // 2. methods // 3. arbitrarily nested on @SecureBinding annotations // Gathering the initial secure values from the type List<String> initialSecureValues = new ArrayList<String>(); for (Annotation annotation : event.getAnnotatedType().getAnnotations()) { collectSecureValues(annotation, initialSecureValues); } for (AnnotatedMethod<?> m : event.getAnnotatedType().getMethods()) { // Gathering the secure values from the method final List<String> values = new ArrayList<String>(initialSecureValues); collectSecureValues(m, values); // If any values have been gathered, adding the annotation to the method and storing it // in the map. if (values.size() > 0) { InterceptSecure is = new InterceptSecureImpl(values.toArray(new String[values.size()])); builder.addToMethod(m.getJavaMember(), is); used = true; interceptSecureForMethods.put(m.getJavaMember(), is); } } // Setting the new annotated type, if any changed were made if (used) { event.setAnnotatedType(builder.create()); } } private void collectSecureValues(AnnotatedMethod m, List<String> values) { for (Annotation annotation : m.getAnnotations()) { collectSecureValues(annotation, values); } } private void collectSecureValues(Annotation annotation, List<String> values) { if (Secure.class.isAssignableFrom(annotation.annotationType())) { values.add(((Secure) annotation).value()); } else { if (annotation.annotationType().getAnnotation(SecureBinding.class) != null) { for (Annotation nestedAnnotation : annotation.annotationType().getAnnotations()) { collectSecureValues(nestedAnnotation, values); } } } } private static class InterceptSecureImpl extends AnnotationLiteral<InterceptSecure> implements InterceptSecure { private final String[] values; private InterceptSecureImpl(String[] values) { this.values = values; } @Override public String[] value() { return values; } } }And for the interceptor:
@Interceptor @InterceptSecure("") public class SecurityInterceptor { @Inject private SecurityExtension se; @AroundInvoke public Object checkSecurity(InvocationContext ctx) throws Exception { // Getting the generated @InterceptSecure annotation for the method. // After the CDI Maintenance Release is released, it should be possible to get the annotated // type of the currently invoked bean, see: // http://old.nabble.com/Retrieving-the-Bean-object-for-an-interceptor-td28147499.html // For now, we just use a map in the extension. One limitation is that this doesn't allow // different security annotations for methods which are used in several beans. InterceptSecure is = se.getInterceptSecure(ctx.getMethod()); String[] toCheck = is == null ? null : is.value(); // Check the el conditions as in the previous post // (...) return ctx.proceed(); } }Adam

