/* * (C) Copyright 2013-2014 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Florent Guillaume */ package org.nuxeo.ecm.core.redis; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.time.Instant; import java.util.Collections; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrBuilder; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.model.ComponentContext; import org.nuxeo.runtime.model.ComponentInstance; import org.nuxeo.runtime.model.DefaultComponent; import org.nuxeo.runtime.model.SimpleContributionRegistry; import org.osgi.framework.Bundle; /** * Implementation of the Redis Service holding the configured Jedis pool. * * @since 5.8 */ public class RedisComponent extends DefaultComponent implements RedisAdmin { private static final String DEFAULT_PREFIX = "nuxeo:"; protected volatile RedisExecutor executor; protected RedisPoolDescriptorRegistry registry = new RedisPoolDescriptorRegistry(); public static class RedisPoolDescriptorRegistry extends SimpleContributionRegistry<RedisPoolDescriptor> { protected RedisPoolDescriptor config; @Override public String getContributionId(RedisPoolDescriptor contrib) { return "main"; } @Override public void contributionUpdated(String id, RedisPoolDescriptor contrib, RedisPoolDescriptor newOrigContrib) { config = contrib; } @Override public void contributionRemoved(String id, RedisPoolDescriptor origContrib) { config = null; } public RedisPoolDescriptor getConfig() { return config; } public void clear() { config = null; } }; protected String delsha; @Override public void activate(ComponentContext context) { super.activate(context); registry.clear(); } @Override public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { if (contribution instanceof RedisPoolDescriptor) { registerRedisPoolDescriptor((RedisPoolDescriptor) contribution); } else { throw new NuxeoException("Unknown contribution class: " + contribution); } } @Override public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { if (contribution instanceof RedisPoolDescriptor) { unregisterRedisPoolDescriptor((RedisPoolDescriptor) contribution); } } public void registerRedisPoolDescriptor(RedisPoolDescriptor contrib) { registry.addContribution(contrib); } public void unregisterRedisPoolDescriptor(RedisPoolDescriptor contrib) { registry.removeContribution(contrib); } @Override public RedisPoolDescriptor getConfig() { return registry.getConfig(); } @Override public void applicationStarted(ComponentContext context) { RedisPoolDescriptor config = getConfig(); if (config == null || config.disabled) { return; } handleNewExecutor(config.newExecutor()); } @Override public void applicationStopped(ComponentContext context, Instant deadline) { if (executor == null) { return; } try { executor.getPool().destroy(); } finally { executor = null; } } @Override public int getApplicationStartedOrder() { return ((DefaultComponent) Framework.getRuntime().getComponentInstance("org.nuxeo.ecm.core.work.service").getInstance()).getApplicationStartedOrder() - 1; } public void handleNewExecutor(RedisExecutor executor) { this.executor = executor; try { delsha = load("org.nuxeo.ecm.core.redis", "del-keys"); } catch (RuntimeException cause) { executor = null; throw new NuxeoException("Cannot activate redis executor", cause); } } @Override public Long clear(final String pattern) { return (Long) executor.evalsha(delsha, Collections.singletonList(pattern), Collections.emptyList()); } @Override public String load(String bundleName, String scriptName) { Bundle b = Framework.getRuntime().getBundle(bundleName); URL loc = b.getEntry(scriptName + ".lua"); if (loc == null) { throw new RuntimeException("Fail to load lua script: " + scriptName); } InputStream is = null; final StrBuilder builder; try { is = loc.openStream(); builder = new StrBuilder(); for (String line : IOUtils.readLines(is)) { builder.appendln(line); } } catch (IOException e) { throw new RuntimeException("Fail to load lua script: " + scriptName, e); } return executor.scriptLoad(builder.toString()); } @Override public <T> T getAdapter(Class<T> adapter) { if (adapter.isAssignableFrom(RedisExecutor.class)) { return adapter.cast(executor); } return super.getAdapter(adapter); } @Override public String namespace(String... names) { RedisPoolDescriptor config = getConfig(); String prefix = config == null ? null : config.prefix; if (StringUtils.isBlank(prefix)) { prefix = DEFAULT_PREFIX; } StringBuilder builder = new StringBuilder(prefix); for (String name : names) { builder.append(name).append(":"); } return builder.toString(); } }