Commit 9d16021c authored by Jeroen Hoffman's avatar Jeroen Hoffman

CMS-10098 Reintegrate branch 'feature/CMS-10098' into master

parents 1219a912 05e62c06
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2013-2015 Hippo B.V. (http://www.onehippo.com)
Copyright 2013-2016 Hippo B.V. (http://www.onehippo.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -49,6 +49,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onehippo.cms7</groupId>
<artifactId>hippo-services</artifactId>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
......
/*
* Copyright 2012-2013 Hippo B.V. (http://www.onehippo.com)
* Copyright 2012-2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -24,6 +24,7 @@ import java.util.List;
import javax.jcr.Session;
import org.hippoecm.repository.util.DateTools;
import org.onehippo.cms7.services.search.jcr.service.HippoJcrSearchService;
import org.onehippo.cms7.services.search.query.constraint.DateConstraint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -36,9 +37,9 @@ public class Filter {
private boolean negated = false;
private final static boolean DEFAULT_FULLTEXT_WILDCARD_POSTFIXED = true;
private final boolean fulltextWildcardPostfixEnabled;
private boolean fulltextWildcardPostfixed = DEFAULT_FULLTEXT_WILDCARD_POSTFIXED;
private final int fulltextWildcardPostfixMinLength;
private final Session session;
......@@ -49,12 +50,18 @@ public class Filter {
* This allows us to change those filters even after those are added to filter
* @see #getJcrExpression()
*/
private List<FilterTypeWrapper> childFilters = new ArrayList<FilterTypeWrapper>();
private List<FilterTypeWrapper> childFilters = new ArrayList<>();
private ChildFilterType firstAddedType;
public Filter(final Session session) {
this(session, HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_ENABLED, HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_MINLENGTH);
}
public Filter(final Session session, final boolean wildcardPostfixEnabled, final int wildcardPostfixMinLength) {
this.session = session;
this.fulltextWildcardPostfixEnabled = wildcardPostfixEnabled;
this.fulltextWildcardPostfixMinLength = wildcardPostfixMinLength;
}
private enum ChildFilterType {
......@@ -69,11 +76,11 @@ public class Filter {
public void addContains(String scope, String fullTextSearch) throws JcrQueryException {
scope = JcrQueryUtils.toXPathProperty(scope, true, "addContains", new String[]{"."});
if(fullTextSearch == null) {
if (fullTextSearch == null) {
throw new JcrQueryException("Not allowed to search on 'null'.");
}
StringBuilder whereClauseBuilder = new StringBuilder();
final StringBuilder containsBuilder = new StringBuilder();
// we rewrite a search for * into a more efficient search
if("*".equals(fullTextSearch)) {
if(".".equals(scope)) {
......@@ -87,28 +94,30 @@ public class Filter {
} else {
String parsedText = FullTextSearchParser.fullTextParseCmsSimpleSearchMode(fullTextSearch, false);
if (fulltextWildcardPostfixed) {
String parsedTextWildCardPostFixed = FullTextSearchParser.fullTextParseCmsSimpleSearchMode(fullTextSearch, true);
if (parsedTextWildCardPostFixed.length() > 0) {
if (fulltextWildcardPostfixEnabled) {
final String parsedTextWildcarded = FullTextSearchParser.fullTextParseCmsSimpleSearchMode(fullTextSearch, true, fulltextWildcardPostfixMinLength);
if (parsedTextWildcarded.length() > 0) {
if (parsedText.length() > 0) {
whereClauseBuilder.append("(");
addContainsToBuilder(whereClauseBuilder, scope, parsedText);
whereClauseBuilder.append(" or ");
addContainsToBuilder(whereClauseBuilder, scope, parsedTextWildCardPostFixed);
whereClauseBuilder.append(")");
containsBuilder.append("(");
addContainsToBuilder(containsBuilder, scope, parsedText);
containsBuilder.append(" or ");
addContainsToBuilder(containsBuilder, scope, parsedTextWildcarded);
containsBuilder.append(")");
} else {
addContainsToBuilder(whereClauseBuilder, scope, parsedTextWildCardPostFixed);
addContainsToBuilder(containsBuilder, scope, parsedTextWildcarded);
}
} else if (whereClauseBuilder.length() > 0) {
addContainsToBuilder(whereClauseBuilder, scope, whereClauseBuilder.toString());
} else if (parsedText.length() > 0) {
addContainsToBuilder(containsBuilder, scope, parsedText);
}
} else if (whereClauseBuilder.length() > 0) {
addContainsToBuilder(whereClauseBuilder, scope, whereClauseBuilder.toString());
} else if (parsedText.length() > 0) {
addContainsToBuilder(containsBuilder, scope, parsedText);
}
log.info("Translated fullTextSearch '{}' to where clause '{}'.", fullTextSearch, whereClauseBuilder.toString());
log.info("Translated fullTextSearch '{}' to function '{}' with wildcarding={} and minimum length={}.",
fullTextSearch, containsBuilder.toString(), fulltextWildcardPostfixEnabled, fulltextWildcardPostfixMinLength);
}
if (whereClauseBuilder.length() > 0) {
addExpression(whereClauseBuilder.toString());
if (containsBuilder.length() > 0) {
addExpression(containsBuilder.toString());
}
}
......@@ -312,7 +321,7 @@ public class Filter {
return;
}
if(builder.length() == 0) {
builder.append("(").append(filter.getJcrExpression()).append(")");;
builder.append("(").append(filter.getJcrExpression()).append(")");
} else {
builder.append(" and ").append("(").append(filter.getJcrExpression()).append(")");
}
......@@ -371,7 +380,6 @@ public class Filter {
/**
* Process AND or OR filters
* @return jcr query expression or null
*/
private void processChildFilters(StringBuilder childFiltersExpression) {
for (FilterTypeWrapper filter : childFilters) {
......
/*
* Copyright 2012-2013 Hippo B.V. (http://www.onehippo.com)
* Copyright 2012-2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,12 +18,12 @@ package org.onehippo.cms7.services.search.jcr.query;
import java.util.StringTokenizer;
import org.apache.commons.lang.StringUtils;
import org.onehippo.cms7.services.search.jcr.service.HippoJcrSearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Input utilities for user searches.
*
*/
public final class FullTextSearchParser {
......@@ -35,8 +35,6 @@ public final class FullTextSearchParser {
private final static String ignoredChars = DEFAULT_IGNORED_CHARS;
private final static int minimalLength = 3;
private static final String WHITESPACE_PATTERN = "\\s+";
private FullTextSearchParser() {
......@@ -63,6 +61,11 @@ public final class FullTextSearchParser {
}
public static String fullTextParseCmsSimpleSearchMode(String value, final boolean wildcardPostfix) {
return fullTextParseCmsSimpleSearchMode(value, wildcardPostfix, HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_MINLENGTH);
}
public static String fullTextParseCmsSimpleSearchMode(String value, final boolean wildcardPostfix, final int minimalLength) {
value = foldToASCIIReplacer(value.trim());
StringBuilder whereClauseBuilder = new StringBuilder();
boolean isOperatorToken;
......@@ -108,8 +111,9 @@ public final class FullTextSearchParser {
isOperatorToken = false;
}
if (wildcardPostfix && tb.length() < getMinimalLength() && !isOperatorToken) {
// for wildcard postfixing we demand the term to be at least as long as #getMinimalLength()
if (wildcardPostfix && tb.length() < minimalLength && !isOperatorToken) {
// for wildcard postfixing we demand the term to be at least as long as minimal length
continue;
}
......@@ -144,15 +148,21 @@ public final class FullTextSearchParser {
// 1: WildcardSearch is set to true
// 2: The term length is at least equal to minimal length: This is to avoid expensive kind of a* searches
// 3: The term is not an operator token like AND or OR
if (wildcardPostfix && tb.length() >= getMinimalLength() && !isOperatorToken) {
if (wildcardPostfix && tb.length() >= minimalLength && !isOperatorToken) {
whereClauseBuilder.append('*');
}
}
return whereClauseBuilder.toString();
}
/**
* Return the default minimal length
*
* @deprecated please use HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_MINLENGTH
*/
@Deprecated
public static int getMinimalLength() {
return minimalLength;
return HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_MINLENGTH;
}
......
/*
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
* Copyright 2012-2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -41,13 +41,18 @@ public class JcrQueryBuilder {
private List<String> excludeScopes = new ArrayList<String>();
private List<String> orderByList = new ArrayList<String>();
private final boolean wildcardPostfixEnabled;
private final int wildcardPostfixMinLength;
private Filter filter;
private String nodeType;
private List<String> selected = new ArrayList<String>();
public JcrQueryBuilder(Session session) {
public JcrQueryBuilder(final Session session, final boolean wildcardPostfixEnabled, final int wildcardPostfixMinLength) {
this.session = session;
this.wildcardPostfixEnabled = wildcardPostfixEnabled;
this.wildcardPostfixMinLength = wildcardPostfixMinLength;
}
public String getNodeType() {
......@@ -226,4 +231,11 @@ public class JcrQueryBuilder {
this.limit = limit;
}
public boolean isWildcardPostfixEnabled() {
return wildcardPostfixEnabled;
}
public int getWildcardPostfixMinLength() {
return wildcardPostfixMinLength;
}
}
/*
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
* Copyright 2012-2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -197,7 +197,8 @@ public class JcrQueryVisitor implements QueryVisitor {
}
private Filter getFilter(final TextConstraint constraint) throws JcrQueryException {
Filter filter = new Filter(session);
final Filter filter = new Filter(session, builder.isWildcardPostfixEnabled(), builder.getWildcardPostfixMinLength());
String property = constraint.getProperty();
if (property == null) {
property = ".";
......
/*
* Copyright 2012-2013 Hippo B.V. (http://www.onehippo.com)
* Copyright 2012-2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -40,10 +40,31 @@ import org.slf4j.LoggerFactory;
public class HippoJcrSearchService implements SearchService, QueryPersistService {
static final Logger log = LoggerFactory.getLogger(HippoJcrSearchService.class);
private static final Logger log = LoggerFactory.getLogger(HippoJcrSearchService.class);
public static final boolean DEFAULT_WILDCARD_POSTFIX_ENABLED = true;
public static final int DEFAULT_WILDCARD_POSTFIX_MINLENGTH = 3;
private boolean wildcardPostfixEnabled = DEFAULT_WILDCARD_POSTFIX_ENABLED;
private int wildcardPostfixMinLength = DEFAULT_WILDCARD_POSTFIX_MINLENGTH;
private Session session;
/**
* Default constructor
*/
public HippoJcrSearchService() {
}
/**
* Parameterized constructor
*/
public HippoJcrSearchService(final Session session, final boolean wildcardPostfixEnabled, final int wildcardPostfixMinLength) {
this.session = session;
this.wildcardPostfixEnabled = wildcardPostfixEnabled;
this.wildcardPostfixMinLength = wildcardPostfixMinLength;
}
public Session getSession() {
return session;
}
......@@ -68,7 +89,7 @@ public class HippoJcrSearchService implements SearchService, QueryPersistService
throw new IllegalArgumentException("Search service only accepts queries created by itself");
}
QueryNode query = (QueryNode) searchQuery;
JcrQueryBuilder queryBuilder = new JcrQueryBuilder(session);
final JcrQueryBuilder queryBuilder = new JcrQueryBuilder(session, this.isWildcardPostfixEnabled(), this.getWildcardPostfixMinLength());
query.accept(new JcrQueryVisitor(queryBuilder, session));
final String queryString = queryBuilder.getQueryString();
try {
......@@ -122,4 +143,13 @@ public class HippoJcrSearchService implements SearchService, QueryPersistService
throw new SearchServiceException(re);
}
}
public boolean isWildcardPostfixEnabled() {
return wildcardPostfixEnabled;
}
public int getWildcardPostfixMinLength() {
return wildcardPostfixMinLength;
}
}
/*
* Copyright 2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onehippo.cms7.services.search.jcr.service;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.onehippo.cms7.services.HippoServiceRegistry;
import org.onehippo.cms7.services.search.service.QueryPersistService;
import org.onehippo.cms7.services.search.service.SearchService;
import org.onehippo.cms7.services.search.service.SearchServiceException;
import org.onehippo.cms7.services.search.service.SearchServiceFactory;
import org.onehippo.repository.modules.AbstractReconfigurableDaemonModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Reconfigurable daemon module that registers the SearchServiceFactory.
*/
public class SearchServiceModule extends AbstractReconfigurableDaemonModule {
private static final Logger log = LoggerFactory.getLogger(SearchServiceModule.class);
private SearchServiceFactory searchServiceFactory;
private boolean wildcardPostfixEnabled;
private int wildcardPostfixMinLength;
@Override
protected void doInitialize(final Session session) throws RepositoryException {
log.info("Initializing search service module");
}
private void register() {
log.debug("Registering search service factory");
searchServiceFactory = new SearchServiceFactory() {
@Override
public SearchService createSearchService(final Object clientObject) throws SearchServiceException {
if (!(clientObject instanceof Session)) {
throw new SearchServiceException("Search service argument must be of type javax.jcr.Session");
}
return new HippoJcrSearchService((Session) clientObject, wildcardPostfixEnabled, wildcardPostfixMinLength);
}
@Override
public QueryPersistService createQueryPersistService(final Object clientObject) throws SearchServiceException {
if (!(clientObject instanceof Session)) {
throw new SearchServiceException("Search service argument must be of type javax.jcr.Session");
}
return new HippoJcrSearchService((Session) clientObject, wildcardPostfixEnabled, wildcardPostfixMinLength);
}
};
HippoServiceRegistry.registerService(searchServiceFactory, SearchServiceFactory.class, SearchServiceFactory.class.getName());
}
@Override
protected void doShutdown() {
unregister();
}
@Override
protected void doConfigure(final Node node) throws RepositoryException {
unregister();
this.wildcardPostfixEnabled = getBoolean(node, "wildcard.postfix.enabled", HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_ENABLED);
this.wildcardPostfixMinLength = getInteger(node, "wildcard.postfix.minlength", HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_MINLENGTH);
log.info("SearchServiceModule configured at wildcardPostfixEnabled={} and wildcardPostfixMinLength={}",
this.wildcardPostfixEnabled, this.wildcardPostfixMinLength);
register();
}
private void unregister() {
log.debug("Unregistering search service factory {}", searchServiceFactory);
if (searchServiceFactory != null) {
HippoServiceRegistry.unregisterService(searchServiceFactory, SearchServiceFactory.class, SearchServiceFactory.class.getName());
}
}
private int getInteger(final Node config, final String property, final int defaultValue) throws RepositoryException {
if (config.hasProperty(property)) {
return (int)config.getProperty(property).getLong();
}
return defaultValue;
}
private boolean getBoolean(final Node config, final String property, final boolean defaultValue) throws RepositoryException {
if (config.hasProperty(property)) {
return config.getProperty(property).getBoolean();
}
return defaultValue;
}
}
......@@ -25,4 +25,19 @@
</sv:property>
</sv:node>
<sv:node sv:name="hippo-configuration-hippo-modules-search-service">
<sv:property sv:name="jcr:primaryType" sv:type="Name">
<sv:value>hippo:initializeitem</sv:value>
</sv:property>
<sv:property sv:name="hippo:sequence" sv:type="Double">
<sv:value>1009</sv:value>
</sv:property>
<sv:property sv:name="hippo:contentresource" sv:type="String">
<sv:value>search-service.xml</sv:value>
</sv:property>
<sv:property sv:name="hippo:contentroot" sv:type="String">
<sv:value>/hippo:configuration/hippo:modules</sv:value>
</sv:property>
</sv:node>
</sv:node>
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2016 Hippo B.V. (http://www.onehippo.com)
-->
<sv:node sv:name="search-service" xmlns:sv="http://www.jcp.org/jcr/sv/1.0">
<sv:property sv:name="jcr:primaryType" sv:type="Name">
<sv:value>hipposys:module</sv:value>
</sv:property>
<sv:property sv:name="hipposys:className" sv:type="String">
<sv:value>org.onehippo.cms7.services.search.jcr.service.SearchServiceModule</sv:value>
</sv:property>
<sv:property sv:name="hipposys:cmsonly" sv:type="Boolean">
<sv:value>true</sv:value>
</sv:property>
<sv:node sv:name="hippo:moduleconfig">
<sv:property sv:name="jcr:primaryType" sv:type="Name">
<sv:value>hipposys:moduleconfig</sv:value>
</sv:property>
<sv:property sv:name="wildcard.postfix.enabled" sv:type="Boolean">
<sv:value>true</sv:value>
</sv:property>
<sv:property sv:name="wildcard.postfix.minlength" sv:type="Long">
<sv:value>3</sv:value>
</sv:property>
</sv:node>
</sv:node>
/*
* Copyright 2015 Hippo B.V. (http://www.onehippo.com)
* Copyright 2015-2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -20,10 +20,18 @@ import javax.jcr.Session;
import org.onehippo.cms7.services.search.commons.query.QueryImpl;
import org.onehippo.cms7.services.search.jcr.query.JcrQueryBuilder;
import org.onehippo.cms7.services.search.jcr.query.JcrQueryVisitor;
import org.onehippo.cms7.services.search.jcr.service.HippoJcrSearchService;
public class TestUtils {
public static String getQueryAsString(final QueryImpl query, final Session session) {
final JcrQueryBuilder builder = new JcrQueryBuilder(session);
return getQueryAsString(query, session, HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_ENABLED, HippoJcrSearchService.DEFAULT_WILDCARD_POSTFIX_MINLENGTH);
}
public static String getQueryAsString(final QueryImpl query, final Session session,
final boolean wildcardPostfixEnabled, final int wildcardPostfixMinlenth) {
final JcrQueryBuilder builder = new JcrQueryBuilder(session,
wildcardPostfixEnabled, wildcardPostfixMinlenth);
JcrQueryVisitor visitor = new JcrQueryVisitor(builder, session);
query.accept(visitor);
return builder.getQueryString();
......
/*
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
* Copyright 2012-2016 Hippo B.V. (http://www.onehippo.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -199,4 +199,70 @@ public class TestJcrQueryBuilder extends RepositoryTestCase {
assertEquals("//*[" + COMMON_SCOPE + " and (((jcr:contains(.,'this') or jcr:contains(.,'this*'))))]" + COMMON_ORDERBY, queryAsString);
}
}
@Test
public void test_contains_with_small_term_with_wildcarding() {
InitialQueryImpl initialQuery = new InitialQueryImpl();
QueryImpl query = initialQuery.from("/").where(QueryUtils.text().contains("ab"));
// small terms are not wildcarded
String queryAsString = TestUtils.getQueryAsString(query, session, true, 3);
assertEquals("//*[" + COMMON_SCOPE + " and (jcr:contains(.,'ab'))]" + COMMON_ORDERBY, queryAsString);
}
@Test
public void test_contains_with_small_term_without_wildcarding() {
InitialQueryImpl initialQuery = new InitialQueryImpl();
QueryImpl query = initialQuery.from("/").where(QueryUtils.text().contains("ab"));
String queryAsString = TestUtils.getQueryAsString(query, session, false, 3);
assertEquals("//*[" + COMMON_SCOPE + " and (jcr:contains(.,'ab'))]" + COMMON_ORDERBY, queryAsString);
}
@Test
public void test_contains_with_big_term_with_wildcarding_at_min_length() {
InitialQueryImpl initialQuery = new InitialQueryImpl();
QueryImpl query = initialQuery.from("/").where(QueryUtils.text().contains("abcdefghij"));
String queryAsString = TestUtils.getQueryAsString(query, session, true, "abcdefghij".length());
assertEquals("//*[" + COMMON_SCOPE + " and ((jcr:contains(.,'abcdefghij') or jcr:contains(.,'abcdefghij*')))]" + COMMON_ORDERBY, queryAsString);
}
@Test
public void test_contains_with_big_term_with_wildcarding_at_min_length_plus_one() {
InitialQueryImpl initialQuery = new InitialQueryImpl();
QueryImpl query = initialQuery.from("/").where(QueryUtils.text().contains("abcdefghij"));
String queryAsString = TestUtils.getQueryAsString(query, session, true, "abcdefghij".length() + 1);
assertEquals("//*[" + COMMON_SCOPE + " and (jcr:contains(.,'abcdefghij'))]" + COMMON_ORDERBY, queryAsString);
}
@Test
public void test_contains_with_big_term_without_wildcarding() {
InitialQueryImpl initialQuery = new InitialQueryImpl();
QueryImpl query = initialQuery.from("/").where(QueryUtils.text().contains("abcdefghij"));
String queryAsString = TestUtils.getQueryAsString(query, session, false, 99999999/*not used!*/);
assertEquals("//*[" + COMMON_SCOPE + " and (jcr:contains(.,'abcdefghij'))]" + COMMON_ORDERBY, queryAsString);
}
@Test
public void test_contains_with_mixed_terms_with_wildcarding() {
InitialQueryImpl initialQuery = new InitialQueryImpl();
QueryImpl query = initialQuery.from("/").where(QueryUtils.text().contains("12 123456"));
String queryAsString = TestUtils.getQueryAsString(query, session);
assertEquals("//*[" + COMMON_SCOPE + " and ((jcr:contains(.,'12 123456') or jcr:contains(.,'123456*')))]" + COMMON_ORDERBY, queryAsString);
}
@Test
public void test_contains_with_mixed_terms_without_wildcarding() {
InitialQueryImpl initialQuery = new InitialQueryImpl();
QueryImpl query = initialQuery.from("/").where(QueryUtils.text().contains("12 123456"));
String queryAsString = TestUtils.getQueryAsString(query, session, false, -1);
assertEquals("//*[" + COMMON_SCOPE + " and (jcr:contains(.,'12 123456'))]" + COMMON_ORDERBY, queryAsString);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
Copyright 2012-2016 Hippo B.V. (http://www.onehippo.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......@@ -55,6 +55,7 @@
<cglib.version>2.2.2</cglib.version>
<hippo.repository.version>4.2.0-SNAPSHOT</hippo.repository.version>
<hippo.services.version>3.2.0-SNAPSHOT</hippo.services.version>
<hippo.utilities.version>3.2.0-SNAPSHOT</hippo.utilities.version>
<maven.plugin.site.version>3.0-beta-3</maven.plugin.site.version>
......@@ -102,6 +103,13 @@
<version>${hippo.utilities.version}</version>
</dependency>
<dependency>
<groupId>org.onehippo.cms7</groupId>
<artifactId>hippo-services</artifactId>
<version>${hippo.services.version}</version>
<scope>provided</scope>
</dependency>
<!-- Apache Commons -->
<dependency>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment