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