/* * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-2.1.html * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Contributors: * Florent Guillaume */ package org.nuxeo.ecm.core.redis; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.net.URISyntaxException; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.nuxeo.common.utils.TextTemplate; import org.nuxeo.common.xmap.DOMSerializer; import org.nuxeo.common.xmap.XMap; import org.nuxeo.ecm.core.cache.CacheFeature; import org.nuxeo.ecm.core.redis.embedded.RedisEmbeddedGuessConnectionError; import org.nuxeo.ecm.core.redis.embedded.RedisEmbeddedPool; import org.nuxeo.ecm.core.redis.embedded.RedisEmbeddedSynchronizedExecutor; import org.nuxeo.ecm.core.redis.embedded.RedisEmbeddedTraceExecutor; import org.nuxeo.ecm.core.test.CoreFeature; import org.nuxeo.ecm.core.test.DefaultRepositoryInit; import org.nuxeo.ecm.core.test.annotations.RepositoryConfig; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.model.RuntimeContext; import org.nuxeo.runtime.test.InlineRef; import org.nuxeo.runtime.test.protocols.inline.InlineURLFactory; import org.nuxeo.runtime.test.runner.Defaults; import org.nuxeo.runtime.test.runner.Features; import org.nuxeo.runtime.test.runner.FeaturesRunner; import org.nuxeo.runtime.test.runner.RuntimeFeature; import org.nuxeo.runtime.test.runner.RuntimeHarness; import org.nuxeo.runtime.test.runner.SimpleFeature; import org.w3c.dom.Document; import org.w3c.dom.Element; @Features({ CoreFeature.class, CacheFeature.class }) @RepositoryConfig(init = DefaultRepositoryInit.class) public class RedisFeature extends SimpleFeature { /** * This defines configuration that can be used to run Redis tests with a * given Redis configured. * * @since 6.0 */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) public @interface Config { Mode mode() default Mode.embedded; String host() default "localhost"; int port() default 0; String prefix() default "nuxeo:test:"; long failoverTimeout() default -1; Class<? extends RedisEmbeddedGuessConnectionError> guessError() default RedisEmbeddedGuessConnectionError.NoError.class; } public enum Mode { disabled, embedded, server, sentinel } public RedisFeature() { } public RedisFeature(Config config) { this.config = config; } protected RedisServerDescriptor newRedisServerDescriptor() { RedisServerDescriptor desc = new RedisServerDescriptor(); desc.hosts = new RedisHostDescriptor[] { new RedisHostDescriptor( config.host(), config.port()) }; return desc; } protected RedisSentinelDescriptor newRedisSentinelDescriptor() { RedisSentinelDescriptor desc = new RedisSentinelDescriptor(); desc.master = "mymaster"; desc.hosts = new RedisHostDescriptor[] { new RedisHostDescriptor( config.host(), config.port()) }; return desc; } public static void clear() throws IOException { final RedisAdmin admin = Framework.getService(RedisAdmin.class); admin.clear("*"); } public static boolean setup(RuntimeHarness harness) throws Exception { return new RedisFeature(Defaults.of(Config.class)).setupMe(harness); } protected boolean setupMe(RuntimeHarness harness) throws Exception { if (Mode.disabled.equals(config.mode())) { return false; } if (harness.getOSGiAdapter().getBundle("org.nuxeo.ecm.core.event") == null) { harness.deployBundle("org.nuxeo.ecm.core.event"); } if (harness.getOSGiAdapter().getBundle("org.nuxeo.ecm.core.storage") == null) { harness.deployBundle("org.nuxeo.ecm.core.storage"); } if (harness.getOSGiAdapter().getBundle("org.nuxeo.ecm.core.cache") == null) { harness.deployBundle("org.nuxeo.ecm.core.cache"); } harness.deployBundle("org.nuxeo.ecm.core.redis"); harness.deployTestContrib("org.nuxeo.ecm.core.redis", RedisFeature.class.getResource("/redis-contribs.xml")); if (Mode.embedded.equals(config.mode())) { RedisComponent component = (RedisComponent) Framework.getRuntime().getComponent(RedisComponent.class.getPackage().getName()); RedisExecutor executor = new RedisPoolExecutor(new RedisEmbeddedPool()); executor = new RedisEmbeddedTraceExecutor(executor); executor = new RedisEmbeddedSynchronizedExecutor(executor); executor = new RedisFailoverExecutor(10, executor); component.handleNewExecutor(executor); } else { RuntimeContext context = Framework.getRuntime().getContext(); context.deploy(toDescriptor(config)); } clear(); return true; } private InlineRef toDescriptor(Config config) throws IOException, URISyntaxException, ParserConfigurationException { File sourceFile = new File(RedisFeature.class.getResource( "/redis-config.xml").toURI()); TextTemplate template = new TextTemplate(); template.setVariable("timeout", Long.toString(config.failoverTimeout())); RedisServerDescriptor desc = null; XMap xmap = new XMap(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); Element extension = (Element)doc.appendChild(doc.createElement("extension")); extension.setAttribute("target", "org.nuxeo.ecm.core.redis.RedisConnector"); extension.setAttribute("point", "configuration"); switch (config.mode()) { case sentinel: xmap.register(RedisSentinelDescriptor.class); desc = newRedisSentinelDescriptor(); break; case server: extension.setAttribute("target", "org.nuxeo.ecm.core.redis.RedisSentinel"); xmap.register(RedisServerDescriptor.class); desc = newRedisServerDescriptor(); break; default: break; } String ext = ""; if (desc != null) { xmap.toXML(desc, doc.getDocumentElement()); ext = DOMSerializer.toStringOmitXml((Element)doc.getDocumentElement().getChildNodes().item(0)); } template.setVariable("extension", ext); ByteArrayOutputStream bos = new ByteArrayOutputStream(); template.process(new FileInputStream(sourceFile), bos); return new InlineRef("redis-test", bos.toString()); } protected Config config; @Override public void initialize(FeaturesRunner runner) throws Exception { config = runner.getConfig(Config.class); runner.getFeature(CacheFeature.class).enable(); InlineURLFactory.install(); } @Override public void start(FeaturesRunner runner) throws Exception { setupMe(runner.getFeature(RuntimeFeature.class).getHarness()); } }