1

I have a vraptor4 project and i want to use apache velocity as the template engine.

So i specialized the br.com.caelum.vraptor.view.DefaultPathResolver as

@Specializes
public class VelocityPathResolver extends DefaultPathResolver {

    @Inject
    protected VelocityPathResolver(FormatResolver resolver) {
        super(resolver);
    }

    protected String getPrefix() {
        return "/WEB-INF/vm/";
    }

    protected String getExtension() {
        return "vm";
    }
}

That work fine, but I cannot have @Named components in my templates.

Having

@SessionScoped
@Named("mycmp")
public class MyComponent implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name = "My Component";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

I cannot refer to it as ${mycmp.name} in my velocity template (.vm), but if i use .jsp it works fine.

To solve it i specialized the br.com.caelum.vraptor.core.DefaultResult as

@Specializes
public class VelocityResult extends DefaultResult {

    private final MyComponent mycmp;

    @Inject
    public VelocityResult(HttpServletRequest request, Container container, ExceptionMapper exceptions, TypeNameExtractor extractor,
                      Messages messages, MyComponent mycmp) {

        super(request, container, exceptions, extractor, messages);
        this.mycmp = mycmp;
    }

    @PostConstruct
    public void init() {
        include("mycmp", mycmp);
    }
}

Is there a better approach for having @Named components in velocity templates?

pbaris
  • 4,525
  • 5
  • 37
  • 61

1 Answers1

1

it looks like that CDI's @Named won't work with velocity templates, but you can implement an Interceptor to do this job for you. An example would be:

@Intercepts
public class IncluderInterceptor {

    @Inject private MyComponent mycmp;

    @AfterCall public void after() {
        result.include("mycmp", mycmp);
        // any other bean you want to
    }
}

And thinking in a more flexible solution, you could create an annotation and use it to define which bean should be included... something like that:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Included {
}

So you could add @Included at your classes:

@Included public class MyComponent { ... }

And just add the accept method on the IncluderInterceptor:

@Intercepts
public class IncluderInterceptor {

    @Inject @Any Instance<Object> allBeans;

    @AfterCall public void after() {
        // foreach allBeans, if has @Included, so
        // include bean.getClass().getSimpleName()
        // with first letter lower case, or something
    }
}

Sure, if you'll include just a few beans the first solution should be enought. Best regards

Turini
  • 71
  • 4
  • With the first solution i can add MyComponent in result. But I don't get how the second solution works. With the second solution i add the controller in result not MyComponent. Or mai i missing something? – pbaris May 26 '15 at 07:26
  • sorry @pbaris. I've just edited my answer. Of course, in that case it would be not be performatic at all. You could inject directly only the beans that contains `@Included` or something like that – Turini May 26 '15 at 13:35