Custom BeanNameResolver cannot handle a null base Object with identifier 'name' - el

I am trying to run EL 3.0 in standalone mode inside a servlet on Tomcat 8 using the following code -
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("name", "Test1");
ELProcessor processor = new ELProcessor();
processor.getELManager().addBeanNameResolver(new BeanNameResolver() {
#Override
public Object getBean(String beanName) {
return request.getAttribute(beanName);
}
});
response.getWriter().write((String)processor.eval("name"));
}
Various articles on EL 3.0 mention that in order to resolve a bean it should be first defined using ELProcessor.defineBean(). However with custom bean resolver as defined above it should be able to resolve bean from request. When I run this code on tomcat 8, it shows following error -
javax.el.PropertyNotFoundException: ELResolver cannot handle a null base Object with identifier 'name'
org.apache.el.parser.AstIdentifier.getValue(AstIdentifier.java:100)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:187)
javax.el.ELProcessor.getValue(ELProcessor.java:45)
javax.el.ELProcessor.eval(ELProcessor.java:38)
org.koyad.servlet.TestELServlet.doGet(TestELServlet.java:59)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Could someone please explain this behaviour?

The standalone javax.el.ELProcessor will throw this exception if it finds an undefined variable name in the expression condition. You can not even write null checks for undefined variables:
${empty unknownVariable}
will throw an exception if this variable hasn't been set before this. If it has been set to null, then it will return true.
When you are providing a custom BeanNameResolver implementation, you need to override the other methods also. For example the isNameResolved method is used to indicate that your resolver actually solved this value.
I have a custom BeanNameResolver that uses a custom Environment class (basically a map, similar to your request):
public static class EnvironmentBeanNameResolver extends BeanNameResolver{
private Environment environment;
public EnvironmentBeanNameResolver( Environment environment ){
this.environment = environment;
}
#Override
public boolean isNameResolved( String beanName ){
return environment.containsAttribute( beanName );
}
#Override
public Object getBean( String beanName ){
return environment.getAttribute( beanName );
}
#Override
public void setBeanValue( String beanName, Object value ){
environment.setAttribute( beanName, value );
}
#Override
public boolean isReadOnly( String beanName ){
return false;
}
#Override
public boolean canCreateBean( String beanName ){
return true;
}
}
and I use it like this
ELProcessor processor = new ELProcessor();
processor.getELManager().addBeanNameResolver( new EnvironmentBeanNameResolver( environment ) );
boolean result = (boolean)processor.eval( "empty knownVariable" );

Related

JAX-RS security role-based (#RolesAllowed) entity filtering

In a JAX-RS application, some of my resources must be filtered depending on which roles the signed-in user has been assigned to. I'm trying to accomplish this using security annotations (#RolesAllowed, #DenyAll and #PermitAll).
This is what I'm looking for:
public class MyEntity {
public String getPublicString() {
...
}
#RolesAllowed("secretRole")
public String getSecretString() {
...
}
}
#Path("/myResource")
public MyResource {
#GET #Path("/{id}")
public MyEntity get(#PathParam("id") int id) {
...
}
}
Now, everyone (anonymous and logged-in users) can GET MyResource and retrieve MyEntity (per id), but for users in role secretRole, I'd like to see the output (here serialized as JSON):
{
"publicString": "...",
"secretString": "..."
}
And other users (either anonymous or otherwise users not acting on role secretRole) should see just:
{
"publicString": "..."
}
I know Jersey has entity filtering (and an implementation that filters based in security roles).
Unfortunately Liberty (Apache CXF based) has no such feature.
What have I done so far?
Since my solution deals primarily with JSON - using Jackson - I did some work based on Jackson's BeanSerializerModifier. Forget BeanSerializerModifier: it gets called only once per bean type (so the first user defines which properties get serialized for all other users - no, thanks).
Just found another Jackson concept that is applied each time a bean is about to be serialized: PropertyFilter and JsonFilter.
It kind of works, the implementation being very simple:
new SimpleBeanPropertyFilter() {
#Override
protected boolean include(BeanPropertyWriter writer) {
return include((PropertyWriter)writer);
}
#Override
protected boolean include(PropertyWriter writer) {
if (writer.findAnnotation(DenyAll.class) != null) {
return false;
}
RolesAllowed rolesAllowed = writer.findAnnotation(RolesAllowed.class);
if (rolesAllowed != null) {
boolean anyMatch = Arrays.stream(rolesAllowed.value())
.anyMatch(role -> securityContext.isUserInRole(role));
if (!anyMatch) {
return false;
}
}
return true;
}
}
And what's missing?
The Achilles' heel in above implementation is the securityContext reference (expecting an instance of SecurityContext).
I couldn't find means to get hold of a reference to the current security context.
Usually securityContext is #Context injected - either as a method parameter or as a field parameter. None of this is available to a BeanSerializerModifier.
I've managed to inject #Context SecurityContext (both by field or by constructor parameter); it happens to be a ThreadLocalSecurityContext in Liberty. BUT its method isUserInRole only works for the first request (when the ObjectMapper is created); then the reference gets stale and any other invocation throws NPE (inside isUserInRole method; the securityContext is still a valid java object reference; though referencing a stale object).
What are my constraints?
Jersey is not an option for me. I'm bound to Liberty (which is Apache CXF based).
I'm already used to Jackson, but it is not a must. JSON and REST are.
EDIT
HOLD ON: I thought the problem was the securityContext, but perhaps it is not the culprit. In time: I've managed to inject #Context SecurityContext (both by field or by constructor parameter); it happens to be a ThreadLocalSecurityContext, so I suppose it will get the actual principal from threadlocal storage.
BUT now I realized that BeanSerializerModifier#changeProperties gets called just once (for each bean), then the list of changed properties gets reused! I'll look closely at the Jackson specs; maybe I'll switch to JSON-B, as pointed by #Andy McCright (if its PropertyVisibilityStrategy doesn't also cache the result).
EDIT 2
Previous implementation with BeanSerializerModifier:
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
return beanProperties.stream()
.filter(property -> {
if (property.findAnnotation(DenyAll.class) != null) {
return false;
}
RolesAllowed rolesAllowed = property.findAnnotation(RolesAllowed.class);
if (rolesAllowed != null) {
boolean anyMatch = Arrays.stream(rolesAllowed.value())
.anyMatch(role -> securityContext.isUserInRole(role));
if (!anyMatch) {
return false;
}
}
return true;
})
.collect(toList());
}
I've managed to handle an instance of SecurityContext over a ThreadLocal. To this end I've implemented a ContainerRequestFilter:
static final ThreadLocal<SecurityContext> tlSecurityContext = new ThreadLocal<>();
#Provider
public static class SecurityContextSavingRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
tlSecurityContext.set(requestContext.getSecurityContext());
}
}
Then tlSecurityContext.get() can be used as the current SecurityContext.
I don't know, however, if this is invalid or otherwise not recommended by JAX-RS spec.
Beyond this I've also switched to JSON-B (from Jackson) because:
it has better integration with Liberty (both server and client JAX-RS) by means of feature jsonb-1.0;
property filtering is less verbose (than Jackson's PropertyFilter), although less powerful too.
Full solution follows (with comments):
A ContextResolver<Jsonb> to configure Jsonb:
#Provider
public class JsonbConfigContextResolver implements ContextResolver<Jsonb> {
#Override
public Jsonb getContext(Class<?> type) {
return JsonbBuilder.newBuilder().withConfig(getConfig()).build();
}
private JsonbConfig getConfig() {
return new JsonbConfig().withPropertyVisibilityStrategy(new SecurityPropertyVisibilityStrategy());
}
}
A PropertyVisibilityStrategy to implement filtering proper:
public class SecurityPropertyVisibilityStrategy implements PropertyVisibilityStrategy {
#Override
public boolean isVisible(Field field) {
return false;
}
#Override
public boolean isVisible(Method method) {
if (method.getAnnotation(DenyAll.class) != null) {
return false;
}
RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
if (rolesAllowed != null) {
boolean anyMatch = Arrays.stream(rolesAllowed.value())
.anyMatch(role -> isUserInRole(role));
if (!anyMatch) {
return false;
}
}
return true;
}
And finally the ThreadLocal hack itself:
private boolean isUserInRole(String role) {
return securityContext.get().isUserInRole(role);
}
private static final ThreadLocal<SecurityContext> securityContext = new ThreadLocal<>();
#Provider
public static class SecurityContextSavingRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
securityContext.set(requestContext.getSecurityContext());
}
}
}

Camel Java DSL: Update the next polling request param using the value from the response

I am new to Apache camel, this is what I am trying to figure out. In a sample code below, I am trying to use the property - "value" in the request param in next polling request.
String valueFromTheResponse= ""
m.addRouteBuilder(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("timer://foo?period=2)
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.setHeader("Accept", constant("application/json"))
.to("https4://" + <myrequestURL>?param=<valueFromTheResponse>)
.marshal().json(JsonLibrary.Jackson)
.setProperty("value", jsonpath("$.value"))
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
valueFromTheResponse = (String) exchange.getProperty("value");
}
})
}
});
m.run();
What would be the best way to achieve this? or assign the class level variable the property value?
UPDATE: SOLUTION
got it working by adding the following:
.process(new Processor() {
#Override
public void process(final Exchange exchange) throws Exception {
exchange.getIn().setHeader("CamelHttpQuery", buildParamQuery());
}
})
You would need to store the value in a shared field in for example the RouteBuilder class itself, or a shared class instance. And then in the to http endpoint uri, you need to set the param query as a message header instead where you can get that value via a method call.
.setHeader(Exchange.HTTP_QUERY, method(this, "buildParamQuery"))
And then have a method
public String buildParamQuery() {
return "param=" + sharedValue;
}
And then you set this field from the inlined processor with the last value. And mind about the initial value, eg the first poll the value is null so you need to maybe to return an empty string/null from the buildParamQuery method or something else.

Spring Data Redis Error handling with Repository and Cache Configurer

Can I use Spring Data Redis #Repositories, and a CachingConfigurer to implement error handling? For instance -
#Override
public CacheErrorHandler errorHandler() {
return new RedisCacheError();
}
Using the CacheConfigurer above along with the repository as defined below, note the use of CrudRepository operations.
#Repository
public interface ObjectRepository extends CrudRepository<object, String>
I notice that CrudRepository has operations such as save, wondering if that is doing a redis.add() in the background? And basically whether or not the ErrorHandler see below, will actually catch errors on the save operation.
public class RedisCacheError implements CacheErrorHandler {
#Override
public void handleCacheGetError(RuntimeException exception,
Cache cache, Object key) {
log.info("caught error in here: " + exception );
}
#Override
public void handleCachePutError(RuntimeException exception, Cache cache,
Object key, Object value) {
log.info("caught error in here: " + exception );
}
#Override
public void handleCacheEvictError(RuntimeException exception, Cache cache,
Object key) {
//Do something on error while Evict cache
}
#Override
public void handleCacheClearError(RuntimeException exception,Cache cache){
//Do something on error while clearing cache
}
}

camel custom marshalling with dataFormat name in header

I'm having two routes in two separated projects :
First route is setting the header with a data format bean name as a constant :
setHeader("dataFormatBeanName", constant("myFirstList"))
First route :
public class MyTest {
#Configuration
public static class MyTestConfig extends CamelConfiguration {
#Bean(name = "myFirstList")
public DataFormat getMyFirstListDataFormat() {
return new MyFirstListDataFormat();
}
#Bean(name = "mySecondList")
public DataFormat getMySecondListDataFormat() {
return new MySecondListDataFormat();
}
#Bean
public RouteBuilder route() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:testFirstDataFormat").setHeader("dataFormatBeanName", constant("myFirstList")).to("direct:myRoute");
from("direct:testSecondDataFormat").setHeader("dataFormatBeanName", constant("mySecondList")).to("direct:myRoute");
}
};
}
}
}
Second route is supposed to retrieve the bean name from the header and use it as a custom marshaller. Something like :
custom(header("dataFormatBeanName"))
(doesn't compile)
Anyone knows how I'm supposed to get my bean name from the header to use it in the custom method ?
#Component
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal().custom(??????????).to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
After a few more hours searching, here is the solution a found :
No changes in the first class.
Second class uses an anonymous DataFormat in which I retrieve the bean name from the header and get the spring bean from camel context before calling its marshal method.
The AbstractXxxDataFormat class belongs to project2 and is inherited by the Project1 DataFormat.
#Override
public void configure() throws Exception {
final RouteDefinition routedefinition = this.from("direct:myRoute");
routedefinition.marshal(new DataFormat() {
#Override
public void marshal(final Exchange exchange, final Object graph, final OutputStream stream) throws Exception {
AbstractXxxDataFormat myDataFormat = (AbstractGoalDataFormat) getContext().getRegistry().lookupByName(exchange.getIn().getHeader("dataFormatBeanName", String.class));
myDataFormat.marshal(exchange, graph, stream);
}
#Override
public Object unmarshal(final Exchange exchange, final InputStream stream) throws Exception {
return null;
}
});
routedefinition.to("netty4:tcp://{{route.address}}:{{port}}?textline=true&sync=true");
}
If there's any better solution available, I'll be interested.
Have you tried simple("${header.dataFormatBeanName}") to access the header?
Also, rather than passing the format bean name in a header in the first place, why not factor out each .marshal() call into two subroutes (one for formatBeanA and one for formatBeanB) and then call the appropriate subroute rather than setting the header in the first place? I believe this could be a cleaner approach.
If you really need to get it in the route as a variable (as opposed to a predicate to be used in the builder api) you could use an inline processor to extract it:
public class MyRouteBuilder extends RouteBuilder {
public void configure() throws Exception {
from("someEndpoint")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
String beanName = exchange.getHeader("beanNameHeader");
}
});
}
}
Just be careful of scope and concurrency when storing the extracted beanName however.
A collegue of mine (thanks to him) found the definite solution :
set bean name in the exchange properties :
exchange.setProperty("myDataFormat", "myDataFormatAutowiredBean");
retrieve the dataFormat bean with RecipientList pattern and (un)marshal :
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:marshal"));
routedefinition.recipientList(simple("dataformat:${property.myDataFormat}:unmarshal"));
Very concise and works just fine.

How to access I18n Bean from inside a task

I am trying to create a plugin with a task, but I have trouble getting access to an instance of I18bean for retrieving internationalized message. Does anyone has a hint on how to do it ?
Found it. You need to add a constructor with a I18nBeanFactory parameter and use this one for retrieving an I18nBean
public class CreateFileTask implements TaskType {
public I18nBeanFactory i18nBeanFactory;
public CreateFileTask(I18nBeanFactory i18nBeanFactory) {
this.i18nBeanFactory=i18nBeanFactory;
}
#NotNull
#Override
public TaskResult execute(TaskContext taskContext) throws TaskException {
I18nBean i18nBean = i18nBeanFactory.getI18nBean();
i18Bean.getText(...);
}
}