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.Collections; 025 026import org.apache.commons.io.IOUtils; 027import org.apache.commons.lang.StringUtils; 028import org.apache.commons.lang.text.StrBuilder; 029import org.nuxeo.ecm.core.api.NuxeoException; 030import org.nuxeo.runtime.RuntimeServiceEvent; 031import org.nuxeo.runtime.RuntimeServiceListener; 032import org.nuxeo.runtime.api.Framework; 033import org.nuxeo.runtime.model.ComponentContext; 034import org.nuxeo.runtime.model.ComponentInstance; 035import org.nuxeo.runtime.model.DefaultComponent; 036import org.nuxeo.runtime.model.SimpleContributionRegistry; 037import org.osgi.framework.Bundle; 038 039/** 040 * Implementation of the Redis Service holding the configured Jedis pool. 041 * 042 * @since 5.8 043 */ 044public class RedisComponent extends DefaultComponent implements RedisAdmin { 045 046 private static final String DEFAULT_PREFIX = "nuxeo:"; 047 048 protected volatile RedisExecutor executor = RedisExecutor.NOOP; 049 050 protected RedisPoolDescriptorRegistry registry = new RedisPoolDescriptorRegistry(); 051 052 public static class RedisPoolDescriptorRegistry extends SimpleContributionRegistry<RedisPoolDescriptor> { 053 054 protected RedisPoolDescriptor config; 055 056 @Override 057 public String getContributionId(RedisPoolDescriptor contrib) { 058 return "main"; 059 } 060 061 @Override 062 public void contributionUpdated(String id, RedisPoolDescriptor contrib, RedisPoolDescriptor newOrigContrib) { 063 config = contrib; 064 } 065 066 @Override 067 public void contributionRemoved(String id, RedisPoolDescriptor origContrib) { 068 config = null; 069 } 070 071 public RedisPoolDescriptor getConfig() { 072 return config; 073 } 074 075 public void clear() { 076 config = null; 077 } 078 }; 079 080 protected String delsha; 081 082 @Override 083 public void activate(ComponentContext context) { 084 super.activate(context); 085 registry.clear(); 086 } 087 088 @Override 089 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 090 if (contribution instanceof RedisPoolDescriptor) { 091 registerRedisPoolDescriptor((RedisPoolDescriptor) contribution); 092 } else { 093 throw new NuxeoException("Unknown contribution class: " + contribution); 094 } 095 } 096 097 @Override 098 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 099 if (contribution instanceof RedisPoolDescriptor) { 100 unregisterRedisPoolDescriptor((RedisPoolDescriptor) contribution); 101 } 102 } 103 104 public void registerRedisPoolDescriptor(RedisPoolDescriptor contrib) { 105 registry.addContribution(contrib); 106 } 107 108 public void unregisterRedisPoolDescriptor(RedisPoolDescriptor contrib) { 109 registry.removeContribution(contrib); 110 } 111 112 public RedisPoolDescriptor getConfig() { 113 return registry.getConfig(); 114 } 115 116 @Override 117 public void applicationStarted(ComponentContext context) { 118 RedisPoolDescriptor config = getConfig(); 119 if (config == null || config.disabled) { 120 return; 121 } 122 Framework.addListener(new RuntimeServiceListener() { 123 124 @Override 125 public void handleEvent(RuntimeServiceEvent event) { 126 if (event.id != RuntimeServiceEvent.RUNTIME_ABOUT_TO_STOP) { 127 return; 128 } 129 Framework.removeListener(this); 130 try { 131 executor.getPool().destroy(); 132 } finally { 133 executor = null; 134 } 135 } 136 }); 137 handleNewExecutor(config.newExecutor()); 138 } 139 140 @Override 141 public int getApplicationStartedOrder() { 142 return ((DefaultComponent) Framework.getRuntime().getComponentInstance("org.nuxeo.ecm.core.work.service").getInstance()).getApplicationStartedOrder() - 1; 143 } 144 145 public void handleNewExecutor(RedisExecutor executor) { 146 this.executor = executor; 147 try { 148 delsha = load("org.nuxeo.ecm.core.redis", "del-keys"); 149 } catch (RuntimeException cause) { 150 executor = null; 151 throw new NuxeoException("Cannot activate redis executor", cause); 152 } 153 } 154 155 @Override 156 public Long clear(final String pattern) { 157 return (Long) executor.evalsha(delsha, Collections.singletonList(pattern), Collections.emptyList()); 158 } 159 160 @Override 161 public String load(String bundleName, String scriptName) { 162 Bundle b = Framework.getRuntime().getBundle(bundleName); 163 URL loc = b.getEntry(scriptName + ".lua"); 164 if (loc == null) { 165 throw new RuntimeException("Fail to load lua script: " + scriptName); 166 } 167 InputStream is = null; 168 final StrBuilder builder; 169 try { 170 is = loc.openStream(); 171 builder = new StrBuilder(); 172 for (String line : IOUtils.readLines(is)) { 173 builder.appendln(line); 174 } 175 } catch (IOException e) { 176 throw new RuntimeException("Fail to load lua script: " + scriptName, e); 177 } 178 179 return executor.scriptLoad(builder.toString()); 180 } 181 182 @Override 183 public <T> T getAdapter(Class<T> adapter) { 184 if (adapter.isAssignableFrom(RedisExecutor.class)) { 185 return adapter.cast(executor); 186 } 187 return super.getAdapter(adapter); 188 } 189 190 @Override 191 public String namespace(String... names) { 192 RedisPoolDescriptor config = getConfig(); 193 String prefix = config == null ? null : config.prefix; 194 if (StringUtils.isBlank(prefix)) { 195 prefix = DEFAULT_PREFIX; 196 } 197 StringBuilder builder = new StringBuilder(prefix); 198 for (String name : names) { 199 builder.append(name).append(":"); 200 } 201 return builder.toString(); 202 } 203 204}