001/* 002 * (C) Copyright 2013-2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Florent Guillaume 016 */ 017package org.nuxeo.ecm.core.redis; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.URL; 022import java.util.Arrays; 023import java.util.List; 024 025import org.apache.commons.io.IOUtils; 026import org.apache.commons.lang.StringUtils; 027import org.apache.commons.lang.text.StrBuilder; 028import org.nuxeo.ecm.core.api.NuxeoException; 029import org.nuxeo.runtime.RuntimeServiceEvent; 030import org.nuxeo.runtime.RuntimeServiceListener; 031import org.nuxeo.runtime.api.Framework; 032import org.nuxeo.runtime.model.ComponentContext; 033import org.nuxeo.runtime.model.ComponentInstance; 034import org.nuxeo.runtime.model.DefaultComponent; 035import org.nuxeo.runtime.model.SimpleContributionRegistry; 036import org.osgi.framework.Bundle; 037 038import redis.clients.jedis.Jedis; 039 040/** 041 * Implementation of the Redis Service holding the configured Jedis pool. 042 * 043 * @since 5.8 044 */ 045public class RedisComponent extends DefaultComponent implements RedisAdmin { 046 047 private static final String DEFAULT_PREFIX = "nuxeo:"; 048 049 protected volatile RedisExecutor executor = RedisExecutor.NOOP; 050 051 protected RedisPoolDescriptorRegistry registry = new RedisPoolDescriptorRegistry(); 052 053 public static class RedisPoolDescriptorRegistry extends SimpleContributionRegistry<RedisPoolDescriptor> { 054 055 protected RedisPoolDescriptor config; 056 057 @Override 058 public String getContributionId(RedisPoolDescriptor contrib) { 059 return "main"; 060 } 061 062 @Override 063 public void contributionUpdated(String id, RedisPoolDescriptor contrib, RedisPoolDescriptor newOrigContrib) { 064 config = contrib; 065 } 066 067 @Override 068 public void contributionRemoved(String id, RedisPoolDescriptor origContrib) { 069 config = null; 070 } 071 072 public RedisPoolDescriptor getConfig() { 073 return config; 074 } 075 076 public void clear() { 077 config = null; 078 } 079 }; 080 081 protected String delsha; 082 083 @Override 084 public void activate(ComponentContext context) { 085 super.activate(context); 086 registry.clear(); 087 } 088 089 @Override 090 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 091 if (contribution instanceof RedisPoolDescriptor) { 092 registerRedisPoolDescriptor((RedisPoolDescriptor) contribution); 093 } else { 094 throw new NuxeoException("Unknown contribution class: " + contribution); 095 } 096 } 097 098 @Override 099 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 100 if (contribution instanceof RedisPoolDescriptor) { 101 unregisterRedisPoolDescriptor((RedisPoolDescriptor) contribution); 102 } 103 } 104 105 public void registerRedisPoolDescriptor(RedisPoolDescriptor contrib) { 106 registry.addContribution(contrib); 107 } 108 109 public void unregisterRedisPoolDescriptor(RedisPoolDescriptor contrib) { 110 registry.removeContribution(contrib); 111 } 112 113 public RedisPoolDescriptor getConfig() { 114 return registry.getConfig(); 115 } 116 117 @Override 118 public void applicationStarted(ComponentContext context) { 119 RedisPoolDescriptor config = getConfig(); 120 if (config == null || config.disabled) { 121 return; 122 } 123 Framework.addListener(new RuntimeServiceListener() { 124 125 @Override 126 public void handleEvent(RuntimeServiceEvent event) { 127 if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) { 128 return; 129 } 130 Framework.removeListener(this); 131 try { 132 executor.getPool().destroy(); 133 } finally { 134 executor = null; 135 } 136 } 137 }); 138 handleNewExecutor(config.newExecutor()); 139 } 140 141 @Override 142 public int getApplicationStartedOrder() { 143 return ((DefaultComponent) Framework.getRuntime().getComponentInstance("org.nuxeo.ecm.core.work.service").getInstance()).getApplicationStartedOrder() - 1; 144 } 145 146 public void handleNewExecutor(RedisExecutor executor) { 147 this.executor = executor; 148 try { 149 delsha = load("org.nuxeo.ecm.core.redis", "del-keys"); 150 } catch (IOException cause) { 151 executor = null; 152 throw new NuxeoException("Cannot activate redis executor", cause); 153 } 154 } 155 156 @Override 157 public Long clear(final String pattern) { 158 return executor.execute(new RedisCallable<Long>() { 159 @Override 160 public Long call(Jedis jedis) { 161 List<String> keys = Arrays.asList(pattern); 162 List<String> args = Arrays.asList(); 163 return (Long) jedis.evalsha(delsha, keys, args); 164 } 165 }); 166 } 167 168 @Override 169 public String load(String bundleName, String scriptName) throws IOException { 170 Bundle b = Framework.getRuntime().getBundle(bundleName); 171 URL loc = b.getEntry(scriptName + ".lua"); 172 InputStream is = loc.openStream(); 173 final StrBuilder builder = new StrBuilder(); 174 for (String line : IOUtils.readLines(is)) { 175 builder.appendln(line); 176 } 177 return executor.execute(new RedisCallable<String>() { 178 @Override 179 public String call(Jedis jedis) { 180 return jedis.scriptLoad(builder.toString()); 181 } 182 }); 183 } 184 185 @Override 186 public <T> T getAdapter(Class<T> adapter) { 187 if (adapter.isAssignableFrom(RedisExecutor.class)) { 188 return adapter.cast(executor); 189 } 190 return super.getAdapter(adapter); 191 } 192 193 @Override 194 public String namespace(String... names) { 195 RedisPoolDescriptor config = getConfig(); 196 String prefix = config == null ? null : config.prefix; 197 if (StringUtils.isBlank(prefix)) { 198 prefix = DEFAULT_PREFIX; 199 } 200 StringBuilder builder = new StringBuilder(prefix); 201 for (String name : names) { 202 builder.append(name).append(":"); 203 } 204 return builder.toString(); 205 } 206 207}