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