001/* 002 * (C) Copyright 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 * Maxime Hilaire 018 */ 019 020package org.nuxeo.ecm.core.redis.contribs; 021 022import java.io.ByteArrayInputStream; 023import java.io.ByteArrayOutputStream; 024import java.io.IOException; 025import java.io.InputStream; 026import java.io.ObjectInputStream; 027import java.io.ObjectOutputStream; 028import java.io.Serializable; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.ecm.core.api.NuxeoException; 033import org.nuxeo.ecm.core.cache.AbstractCache; 034import org.nuxeo.ecm.core.cache.CacheDescriptor; 035import org.nuxeo.ecm.core.redis.RedisAdmin; 036import org.nuxeo.ecm.core.redis.RedisCallable; 037import org.nuxeo.ecm.core.redis.RedisExecutor; 038import org.nuxeo.runtime.api.Framework; 039 040import redis.clients.jedis.Jedis; 041 042/** 043 * Cache implementation on top of Redis 044 * 045 * @since 6.0 046 */ 047public class RedisCache extends AbstractCache { 048 049 protected static final String UTF_8 = "UTF-8"; 050 051 protected static final Log log = LogFactory.getLog(RedisCache.class); 052 053 protected final RedisExecutor executor; 054 055 protected final String namespace; 056 057 public RedisCache(CacheDescriptor desc) { 058 super(desc); 059 executor = Framework.getService(RedisExecutor.class); 060 namespace = Framework.getService(RedisAdmin.class).namespace("cache", name); 061 } 062 063 protected String formatKey(String key) { 064 return namespace.concat(key); 065 } 066 067 protected Serializable deserializeValue(byte[] workBytes) throws IOException { 068 if (workBytes == null) { 069 return null; 070 } 071 072 InputStream bain = new ByteArrayInputStream(workBytes); 073 ObjectInputStream in = new ObjectInputStream(bain); 074 try { 075 return (Serializable) in.readObject(); 076 } catch (ClassNotFoundException e) { 077 throw new NuxeoException(e); 078 } 079 } 080 081 protected static byte[] bytes(String string) { 082 try { 083 return string.getBytes(UTF_8); 084 } catch (IOException e) { 085 // cannot happen for UTF-8 086 throw new NuxeoException(e); 087 } 088 } 089 090 @Override 091 public Serializable get(final String key) { 092 return executor.execute(new RedisCallable<Serializable>() { 093 @Override 094 public Serializable call(Jedis jedis) { 095 try { 096 return deserializeValue(jedis.get(bytes(formatKey(key)))); 097 } catch (IOException e) { 098 log.error(e); 099 return null; 100 } 101 } 102 }); 103 104 } 105 106 protected byte[] serializeValue(Serializable value) throws IOException { 107 ByteArrayOutputStream baout = new ByteArrayOutputStream(); 108 ObjectOutputStream out = new ObjectOutputStream(baout); 109 out.writeObject(value); 110 out.flush(); 111 out.close(); 112 return baout.toByteArray(); 113 } 114 115 @Override 116 public void invalidate(final String key) { 117 executor.execute(new RedisCallable<Void>() { 118 @Override 119 public Void call(Jedis jedis) { 120 jedis.del(new String[] { formatKey(key) }); 121 return null; 122 } 123 }); 124 } 125 126 @Override 127 public void invalidateAll() { 128 Framework.getService(RedisAdmin.class).clear(formatKey("*")); 129 } 130 131 @Override 132 public void put(final String key, final Serializable value) { 133 executor.execute(new RedisCallable<Void>() { 134 @Override 135 public Void call(Jedis jedis) { 136 try { 137 byte[] bkey = bytes(formatKey(key)); 138 jedis.set(bkey, serializeValue(value)); 139 // Redis set in second ttl but descriptor set as mn 140 int ttlKey = ttl * 60; 141 jedis.expire(bkey, ttlKey); 142 return null; 143 } catch (IOException e) { 144 throw new NuxeoException(e); 145 } 146 } 147 }); 148 } 149 150 @Override 151 public boolean hasEntry(final String key) { 152 return (Boolean) executor.execute(new RedisCallable<Serializable>() { 153 @Override 154 public Serializable call(Jedis jedis) { 155 return jedis.exists(bytes(formatKey(key))); 156 } 157 }); 158 } 159 160}