Hooks - Validation and Caching

This commit is contained in:
avikganguly01
2014-10-16 18:18:47 +05:30
parent 3847186f2b
commit 84c321cfe8
11 changed files with 212 additions and 17 deletions
@@ -0,0 +1,66 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.integrationtests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.apache.http.conn.HttpHostConnectException;
import org.junit.Before;
import org.junit.Test;
import org.mifosplatform.integrationtests.common.HookHelper;
import org.mifosplatform.integrationtests.common.OfficeHelper;
import org.mifosplatform.integrationtests.common.Utils;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.path.json.JsonPath;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
public class HookIntegrationTest {
private RequestSpecification requestSpec;
private ResponseSpecification responseSpec;
private HookHelper hookHelper;
private OfficeHelper officeHelper;
@Before
public void setUp() throws Exception {
Utils.initializeRESTAssured();
this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
this.hookHelper = new HookHelper(this.requestSpec, this.responseSpec);
this.officeHelper = new OfficeHelper(this.requestSpec, this.responseSpec);
}
@Test
public void shouldSendOfficeCreationNotification() {
//Subject to https://echo-webhook.herokuapp.com being up
//See http://www.jamesward.com/2014/06/11/testing-webhooks-was-a-pain-so-i-fixed-the-glitch
final String payloadURL = "http://echo-webhook.herokuapp.com:80/Z7RXoCBdLSFMDrpn?";
this.hookHelper.createHook(payloadURL);
final Integer createdOfficeID = this.officeHelper.createOffice("01 January 2012");
try {
final String json = RestAssured.get(payloadURL.replace("?", "")).asString();
final Integer notificationOfficeId = JsonPath.with(json).get("officeId");
assertEquals("Equality check for created officeId and hook received payload officeId",
createdOfficeID, notificationOfficeId);
System.out.println("Notification Office Id - " + notificationOfficeId);
} catch (final Exception e) {
if (e instanceof HttpHostConnectException) {
fail("Failed to connect to https://echo-webhook.herokuapp.com platform");
}
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,52 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.integrationtests.common;
import java.util.ArrayList;
import java.util.HashMap;
import com.google.gson.Gson;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
public class HookHelper {
private final RequestSpecification requestSpec;
private final ResponseSpecification responseSpec;
private static final String CREATE_HOOK_URL = "/mifosng-provider/api/v1/hooks?" + Utils.TENANT_IDENTIFIER;
public HookHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
this.requestSpec = requestSpec;
this.responseSpec = responseSpec;
}
public Integer createHook(final String payloadURL) {
System.out.println("---------------------------------CREATING A HOOK---------------------------------------------");
return Utils.performServerPost(requestSpec, responseSpec, CREATE_HOOK_URL, getTestHookAsJson(payloadURL),
"resourceId");
}
public String getTestHookAsJson(final String payloadURL) {
final HashMap<String, Object> map = new HashMap<>();
map.put("name", "Web");
map.put("displayName", Utils.randomNameGenerator("Hook_DisplayName_", 5));
map.put("isActive", "true");
final HashMap<String, String> config = new HashMap<>();
config.put("Content Type", "json");
config.put("Payload URL", payloadURL);
map.put("config", config);
final ArrayList<HashMap<String, String>> events = new ArrayList<>();
final HashMap<String, String> createOfficeEvent = new HashMap<>();
createOfficeEvent.put("actionName", "CREATE");
createOfficeEvent.put("entityName", "OFFICE");
events.add(createOfficeEvent);
map.put("events", events);
System.out.println("map : " + map);
return new Gson().toJson(map);
}
}
@@ -41,6 +41,7 @@ public class WebXmlConfiguration {
jerseyServletRegistration.addInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true"); jerseyServletRegistration.addInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters", jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters",
ResponseCorsFilter.class.getName()); ResponseCorsFilter.class.getName());
jerseyServletRegistration.addInitParameter("com.sun.jersey.config.feature.DisableWADL", "true");
// debugging for development: // debugging for development:
// jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", // jerseyServletRegistration.addInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
// LoggingFilter.class.getName()); // LoggingFilter.class.getName());
@@ -65,8 +65,10 @@ public class Hook extends AbstractAuditableCustom<AppUser, Long> {
final Set<HookResource> events) { final Set<HookResource> events) {
final String displayName = command final String displayName = command
.stringValueOfParameterNamed(displayNameParamName); .stringValueOfParameterNamed(displayNameParamName);
final Boolean isActive = command Boolean isActive = command
.booleanObjectValueOfParameterNamed(isActiveParamName); .booleanObjectValueOfParameterNamed(isActiveParamName);
if(isActive == null)
isActive = false;
return new Hook(template, displayName, isActive, config, events); return new Hook(template, displayName, isActive, config, events);
} }
@@ -10,11 +10,11 @@ import java.util.List;
import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant; import org.mifosplatform.infrastructure.core.domain.MifosPlatformTenant;
import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil; import org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil;
import org.mifosplatform.infrastructure.hooks.domain.Hook; import org.mifosplatform.infrastructure.hooks.domain.Hook;
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
import org.mifosplatform.infrastructure.hooks.event.HookEvent; import org.mifosplatform.infrastructure.hooks.event.HookEvent;
import org.mifosplatform.infrastructure.hooks.event.HookEventSource; import org.mifosplatform.infrastructure.hooks.event.HookEventSource;
import org.mifosplatform.infrastructure.hooks.processor.HookProcessor; import org.mifosplatform.infrastructure.hooks.processor.HookProcessor;
import org.mifosplatform.infrastructure.hooks.processor.HookProcessorProvider; import org.mifosplatform.infrastructure.hooks.processor.HookProcessorProvider;
import org.mifosplatform.infrastructure.hooks.service.HookReadPlatformService;
import org.mifosplatform.infrastructure.security.service.TenantDetailsService; import org.mifosplatform.infrastructure.security.service.TenantDetailsService;
import org.mifosplatform.useradministration.domain.AppUser; import org.mifosplatform.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -24,14 +24,14 @@ import org.springframework.stereotype.Service;
public class MifosHookListener implements HookListener { public class MifosHookListener implements HookListener {
private final HookProcessorProvider hookProcessorProvider; private final HookProcessorProvider hookProcessorProvider;
private final HookRepository hookRepository; private final HookReadPlatformService hookReadPlatformService;
private final TenantDetailsService tenantDetailsService; private final TenantDetailsService tenantDetailsService;
@Autowired @Autowired
public MifosHookListener(final HookProcessorProvider hookProcessorProvider, public MifosHookListener(final HookProcessorProvider hookProcessorProvider,
final HookRepository hookRepository, final HookReadPlatformService hookReadPlatformService,
final TenantDetailsService tenantDetailsService) { final TenantDetailsService tenantDetailsService) {
this.hookRepository = hookRepository; this.hookReadPlatformService = hookReadPlatformService;
this.hookProcessorProvider = hookProcessorProvider; this.hookProcessorProvider = hookProcessorProvider;
this.tenantDetailsService = tenantDetailsService; this.tenantDetailsService = tenantDetailsService;
} }
@@ -51,10 +51,11 @@ public class MifosHookListener implements HookListener {
final String entityName = hookEventSource.getEntityName(); final String entityName = hookEventSource.getEntityName();
final String actionName = hookEventSource.getActionName(); final String actionName = hookEventSource.getActionName();
final String payload = event.getPayload(); final String payload = event.getPayload();
final List<Hook> hooks = this.hookReadPlatformService
.retrieveHooksByEvent(hookEventSource.getEntityName(),
hookEventSource.getActionName());
final List<Hook> hooks = this.hookRepository
.findAllHooksListeningToEvent(hookEventSource.getEntityName(),
hookEventSource.getActionName());
for (final Hook hook : hooks) { for (final Hook hook : hooks) {
final HookProcessor processor = this.hookProcessorProvider final HookProcessor processor = this.hookProcessorProvider
.getProcessor(hook); .getProcessor(hook);
@@ -57,7 +57,7 @@ public class WebHookProcessor implements HookProcessor {
private void sendRequest(final String url, final String contentType, private void sendRequest(final String url, final String contentType,
final String payload, final String entityName, final String payload, final String entityName,
final String actionName, final String tenantIdentifier, final String actionName, final String tenantIdentifier,
final String authToken) { @SuppressWarnings("unused") final String authToken) {
final String mifosEndpointUrl = System.getProperty("baseUrl"); final String mifosEndpointUrl = System.getProperty("baseUrl");
final WebHookService service = ProcessorHelper final WebHookService service = ProcessorHelper
@@ -70,13 +70,11 @@ public class WebHookProcessor implements HookProcessor {
|| contentType.contains("json")) { || contentType.contains("json")) {
final JsonObject json = new JsonParser().parse(payload) final JsonObject json = new JsonParser().parse(payload)
.getAsJsonObject(); .getAsJsonObject();
json.addProperty("mifosToken", authToken);
service.sendJsonRequest(entityName, actionName, tenantIdentifier, service.sendJsonRequest(entityName, actionName, tenantIdentifier,
mifosEndpointUrl, json, callback); mifosEndpointUrl, json, callback);
} else { } else {
Map<String, String> map = new HashMap<>(); Map<String, String> map = new HashMap<>();
map = new Gson().fromJson(payload, map.getClass()); map = new Gson().fromJson(payload, map.getClass());
map.put("mifosToken", authToken);
service.sendFormRequest(entityName, actionName, tenantIdentifier, service.sendFormRequest(entityName, actionName, tenantIdentifier,
mifosEndpointUrl, map, callback); mifosEndpointUrl, map, callback);
} }
@@ -14,6 +14,7 @@ import retrofit.client.Response;
import retrofit.http.Body; import retrofit.http.Body;
import retrofit.http.FieldMap; import retrofit.http.FieldMap;
import retrofit.http.FormUrlEncoded; import retrofit.http.FormUrlEncoded;
import retrofit.http.GET;
import retrofit.http.Header; import retrofit.http.Header;
import retrofit.http.POST; import retrofit.http.POST;
@@ -27,6 +28,10 @@ public interface WebHookService {
final static String ENDPOINT_HEADER = "X-Mifos-Endpoint"; final static String ENDPOINT_HEADER = "X-Mifos-Endpoint";
final static String API_KEY_HEADER = "X-Mifos-API-Key"; final static String API_KEY_HEADER = "X-Mifos-API-Key";
// Ping
@GET("/")
Response sendEmptyRequest();
// Template - Web // Template - Web
@POST("/") @POST("/")
void sendJsonRequest(@Header(ENTITY_HEADER) String entityHeader, void sendJsonRequest(@Header(ENTITY_HEADER) String entityHeader,
@@ -6,8 +6,10 @@
package org.mifosplatform.infrastructure.hooks.service; package org.mifosplatform.infrastructure.hooks.service;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import org.mifosplatform.infrastructure.hooks.data.HookData; import org.mifosplatform.infrastructure.hooks.data.HookData;
import org.mifosplatform.infrastructure.hooks.domain.Hook;
public interface HookReadPlatformService { public interface HookReadPlatformService {
@@ -15,5 +17,7 @@ public interface HookReadPlatformService {
HookData retrieveHook(Long hookId); HookData retrieveHook(Long hookId);
HookData retrieveNewHookDetails(String templateName); List<Hook> retrieveHooksByEvent(final String actionName, final String entityName);
HookData retrieveNewHookDetails(String templateName);
} }
@@ -19,9 +19,12 @@ import org.mifosplatform.infrastructure.hooks.data.Field;
import org.mifosplatform.infrastructure.hooks.data.Grouping; import org.mifosplatform.infrastructure.hooks.data.Grouping;
import org.mifosplatform.infrastructure.hooks.data.HookData; import org.mifosplatform.infrastructure.hooks.data.HookData;
import org.mifosplatform.infrastructure.hooks.data.HookTemplateData; import org.mifosplatform.infrastructure.hooks.data.HookTemplateData;
import org.mifosplatform.infrastructure.hooks.domain.Hook;
import org.mifosplatform.infrastructure.hooks.domain.HookRepository;
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException; import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
@@ -31,12 +34,15 @@ import org.springframework.stereotype.Service;
public class HookReadPlatformServiceImpl implements HookReadPlatformService { public class HookReadPlatformServiceImpl implements HookReadPlatformService {
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final HookRepository hookRepository;
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
@Autowired @Autowired
public HookReadPlatformServiceImpl(final PlatformSecurityContext context, public HookReadPlatformServiceImpl(final PlatformSecurityContext context,
final HookRepository hookRepository,
final RoutingDataSource dataSource) { final RoutingDataSource dataSource) {
this.context = context; this.context = context;
this.hookRepository = hookRepository;
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
@@ -64,6 +70,15 @@ public class HookReadPlatformServiceImpl implements HookReadPlatformService {
} }
@Override
@Cacheable(value = "hooks", key = "T(org.mifosplatform.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('HK')")
public List<Hook> retrieveHooksByEvent(final String actionName,
final String entityName) {
return this.hookRepository.findAllHooksListeningToEvent(actionName,
entityName);
}
@Override @Override
public HookData retrieveNewHookDetails(final String templateName) { public HookData retrieveNewHookDetails(final String templateName) {
@@ -7,9 +7,11 @@ package org.mifosplatform.infrastructure.hooks.service;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.actionNameParamName; import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.actionNameParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.configParamName; import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.configParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.contentTypeName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.entityNameParamName; import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.entityNameParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.eventsParamName; import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.eventsParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.nameParamName; import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.nameParamName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.payloadURLName;
import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.webTemplateName; import static org.mifosplatform.infrastructure.hooks.api.HookApiConstants.webTemplateName;
import java.util.ArrayList; import java.util.ArrayList;
@@ -36,13 +38,18 @@ import org.mifosplatform.infrastructure.hooks.domain.HookTemplateRepository;
import org.mifosplatform.infrastructure.hooks.domain.Schema; import org.mifosplatform.infrastructure.hooks.domain.Schema;
import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException; import org.mifosplatform.infrastructure.hooks.exception.HookNotFoundException;
import org.mifosplatform.infrastructure.hooks.exception.HookTemplateNotFoundException; import org.mifosplatform.infrastructure.hooks.exception.HookTemplateNotFoundException;
import org.mifosplatform.infrastructure.hooks.processor.ProcessorHelper;
import org.mifosplatform.infrastructure.hooks.processor.WebHookService;
import org.mifosplatform.infrastructure.hooks.serialization.HookCommandFromApiJsonDeserializer; import org.mifosplatform.infrastructure.hooks.serialization.HookCommandFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import retrofit.RetrofitError;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -72,6 +79,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
@Transactional @Transactional
@Override @Override
@CacheEvict(value = "hooks", allEntries = true)
public CommandProcessingResult createHook(final JsonCommand command) { public CommandProcessingResult createHook(final JsonCommand command) {
try { try {
@@ -90,7 +98,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
final Hook hook = Hook.fromJson(command, template, config, final Hook hook = Hook.fromJson(command, template, config,
allEvents); allEvents);
validateHookRules(template, config); validateHookRules(template, config, allEvents);
this.hookRepository.save(hook); this.hookRepository.save(hook);
@@ -105,6 +113,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
@Transactional @Transactional
@Override @Override
@CacheEvict(value = "hooks", allEntries = true)
public CommandProcessingResult updateHook(final Long hookId, public CommandProcessingResult updateHook(final Long hookId,
final JsonCommand command) { final JsonCommand command) {
@@ -156,6 +165,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
@Transactional @Transactional
@Override @Override
@CacheEvict(value = "hooks", allEntries = true)
public CommandProcessingResult deleteHook(final Long hookId) { public CommandProcessingResult deleteHook(final Long hookId) {
this.context.authenticatedUser(); this.context.authenticatedUser();
@@ -200,7 +210,9 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
for (final Entry<String, String> configEntry : hookConfig.entrySet()) { for (final Entry<String, String> configEntry : hookConfig.entrySet()) {
for (final Schema field : fields) { for (final Schema field : fields) {
if (field.getFieldName().equalsIgnoreCase(configEntry.getKey())) { final String fieldName = field.getFieldName();
if (fieldName.equalsIgnoreCase(configEntry.getKey())) {
final HookConfiguration config = HookConfiguration final HookConfiguration config = HookConfiguration
.createNewWithoutHook(field.getFieldType(), .createNewWithoutHook(field.getFieldType(),
configEntry.getKey(), configEntry.getKey(),
@@ -237,7 +249,7 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
} }
private void validateHookRules(final HookTemplate template, private void validateHookRules(final HookTemplate template,
final Set<HookConfiguration> config) { final Set<HookConfiguration> config, Set<HookResource> events) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder( final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
@@ -250,6 +262,43 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
errorMessage); errorMessage);
} }
for (final HookConfiguration conf : config) {
final String fieldValue = conf.getFieldValue();
if (conf.getFieldName().equals(contentTypeName)) {
if (!(fieldValue.equalsIgnoreCase("json") || fieldValue
.equalsIgnoreCase("form"))) {
final String errorMessage = "content.type.must.be.json.or.form";
baseDataValidator.reset()
.failWithCodeNoParameterAddedToErrorCode(
errorMessage);
}
}
if (conf.getFieldName().equals(payloadURLName)) {
try {
final WebHookService service = ProcessorHelper
.createWebHookService(fieldValue);
service.sendEmptyRequest();
} catch (RetrofitError re) {
// Swallow error if it's because of method not supported or
// if url throws 404 - required for integration test,
// url generated on 1st POST request
if (re.getResponse() == null) {
String errorMessage = "url.invalid";
baseDataValidator.reset()
.failWithCodeNoParameterAddedToErrorCode(
errorMessage);
}
}
}
}
if (events == null || events.isEmpty()) {
final String errorMessage = "registered.events.cannot.be.empty";
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
errorMessage);
}
final Set<Schema> fields = template.getSchema(); final Set<Schema> fields = template.getSchema();
for (final Schema field : fields) { for (final Schema field : fields) {
if (!field.isOptional()) { if (!field.isOptional()) {
@@ -261,8 +310,8 @@ public class HookWritePlatformServiceJpaRepositoryImpl implements
} }
if (!found) { if (!found) {
final String errorMessage = "required.config.field." final String errorMessage = "required.config.field."
+ field.getFieldName() + ".not.provided"; + "not.provided";
baseDataValidator.reset() baseDataValidator.reset().value(field.getFieldName())
.failWithCodeNoParameterAddedToErrorCode( .failWithCodeNoParameterAddedToErrorCode(
errorMessage); errorMessage);
} }
@@ -34,4 +34,6 @@
overflowToDisk="false" /> overflowToDisk="false" />
<cache name="codes" maxEntriesLocalHeap="10000" eternal="true" <cache name="codes" maxEntriesLocalHeap="10000" eternal="true"
overflowToDisk="false" /> overflowToDisk="false" />
<cache name="hooks" maxEntriesLocalHeap="10000" eternal="true"
overflowToDisk="false" />
</ehcache> </ehcache>