Reputation: 1058
I wrote an aspect that I'm trying to test with junit.
The aspect has an @Around advice on a 3rd party method called setQuery
.
At compile time it complains: Can't find referenced pointcut setQuery
Here's my aspect:
@Component
@Aspect
public class ElasticsearchQuerySecurityAspect {
@Around("org.elasticsearch.action.search.SearchRequestBuilder.setQuery() && args(queryBuilder)")
public void addFilter(final ProceedingJoinPoint pjp, QueryBuilder queryBuilder) throws Throwable {
Object[] args = pjp.getArgs();
// Set the filter to use our plugin
FilterBuilder securityFilter = FilterBuilders.scriptFilter("visibility-filter")
.lang("native")
.addParam("visibility-field", "visibility")
.addParam("parameter", "default");
// Re-create original query with the filter applied
QueryBuilder newQuery = QueryBuilders.filteredQuery(queryBuilder,securityFilter);
log.info("Adding filter to search request");
// Tell the method to run with the modified parameter
args[0] = newQuery;
pjp.proceed(args);
}
}
Here's my junit test:
@RunWith(SpringJUnit4ClassRunner.class)// NOTE #1
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@EnableLoadTimeWeaving
@ComponentScan
public class ElasticsearchQuerySecurityTest {
Client client = mock(Client.class);
@Before
public void setUp() throws Exception {
}
@Test
public void test() {
SearchRequestBuilder s = new SearchRequestBuilder(client);
QueryBuilder qb = QueryBuilders.queryString("name:foo");
XContentBuilder builder;
try {
builder = XContentFactory.jsonBuilder();
qb.toXContent(builder, null);
assertEquals("{\"query_string\":{\"query\":\"name:foo\"}}",builder.string());
// Call setQuery() which will invoke the security advice and add a filter to the query
s.setQuery(qb);
builder = XContentFactory.jsonBuilder().startObject();
qb.toXContent(builder, null);
builder.endObject();
assertEquals("{\"query\": "+
"{ \"filtered\": "+
"{ \"query\": "+
"{ \"query_string\": "+
"{ \"name:foo\", } }, "+
"\"filter\": "+
"{ \"script\": "+
"{ \"script\": \"visibility-filter\","+
"\"lang\":\"native\", "+
"\"params\": "+
"{ \"visibility-field\":\"visibility\", "+
"\"parameter\":\"default\" } } } } } }",
builder.string());
} catch (IOException e) {
fail("We threw an I/O exception!");
}
}
}
I also have this aop.xml on the classpath:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<include within="org.elasticsearch.action.search.*"/>
</weaver>
<aspects>
<aspect name="org.omaas.security.ElasticsearchQuerySecurityAspect"/>
</aspects>
</aspectj>
I tried an aspect with @Around("execution(public * set*())")
and found that it only advised stuff in the current package. How do I get it to be applied to stuff in the 3rd-party package?
Upvotes: 1
Views: 2143
Reputation: 67457
Spring AOP can only weave into Spring Beans. As your 3rd party target class is not a Spring bean, there is no way to apply an aspect to it. For that purpose you need to use AspectJ which is way more powerful and does not rely on Spring's "AOP lite" implementation based on dynamic proxies.
With AspectJ you have two options:
Edit: Oh, by the way, your pointcut syntax is invalid. You cannot write
@Around("org.elasticsearch.action.search.SearchRequestBuilder.setQuery() && args(queryBuilder)")
Instead you rather need something like
@Around("execution(* org.elasticsearch.action.search.SearchRequestBuilder.setQuery(*)) && args(queryBuilder)")
A method name is not enough, you have to tell the AOP framework that you want to capture its execution()
(in AspectJ cou could also capture all its callers via call()
). Secondly, you will not capture a method with one QueryBuilder
parameter by specifying a method signature setQuery()
without any parameters, thus I suggest you use setQuery(*)
or, if you want to be even more precise, setQuery(org.elasticsearch.index.query.QueryBuilder)
. You also need a return type and/or modifier like public
in front of the method name or again a joker like *
.
Upvotes: 6