001 /* 002 * JBoss, Home of Professional Open Source. 003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors 004 * as indicated by the @author tags. See the copyright.txt file in the 005 * distribution for a full listing of individual contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 package org.jboss.dna.connector.inmemory; 023 024 import java.io.ByteArrayInputStream; 025 import java.io.ByteArrayOutputStream; 026 import java.io.IOException; 027 import java.io.ObjectInputStream; 028 import java.io.ObjectOutputStream; 029 import java.util.Enumeration; 030 import java.util.HashMap; 031 import java.util.Hashtable; 032 import java.util.Map; 033 import java.util.UUID; 034 import java.util.concurrent.atomic.AtomicInteger; 035 import javax.naming.BinaryRefAddr; 036 import javax.naming.Context; 037 import javax.naming.InitialContext; 038 import javax.naming.NamingException; 039 import javax.naming.RefAddr; 040 import javax.naming.Reference; 041 import javax.naming.StringRefAddr; 042 import javax.naming.spi.ObjectFactory; 043 import net.jcip.annotations.GuardedBy; 044 import org.jboss.dna.common.i18n.I18n; 045 import org.jboss.dna.common.util.CheckArg; 046 import org.jboss.dna.graph.cache.CachePolicy; 047 import org.jboss.dna.graph.connectors.RepositoryConnection; 048 import org.jboss.dna.graph.connectors.RepositoryContext; 049 import org.jboss.dna.graph.connectors.RepositorySource; 050 import org.jboss.dna.graph.connectors.RepositorySourceCapabilities; 051 import org.jboss.dna.graph.connectors.RepositorySourceException; 052 053 /** 054 * @author Randall Hauch 055 */ 056 public class InMemoryRepositorySource implements RepositorySource, ObjectFactory { 057 058 /** 059 * The initial version is 1 060 */ 061 private static final long serialVersionUID = 1L; 062 063 /** 064 * The default limit is {@value} for retrying {@link RepositoryConnection connection} calls to the underlying source. 065 */ 066 public static final int DEFAULT_RETRY_LIMIT = 0; 067 068 protected static final RepositorySourceCapabilities CAPABILITIES = new RepositorySourceCapabilities(true, true); 069 070 protected static final String ROOT_NODE_UUID = "rootNodeUuid"; 071 protected static final String SOURCE_NAME = "sourceName"; 072 protected static final String DEFAULT_CACHE_POLICY = "defaultCachePolicy"; 073 protected static final String JNDI_NAME = "jndiName"; 074 protected static final String RETRY_LIMIT = "retryLimit"; 075 076 @GuardedBy( "sourcesLock" ) 077 private String name; 078 @GuardedBy( "this" ) 079 private String jndiName; 080 private UUID rootNodeUuid = UUID.randomUUID(); 081 private CachePolicy defaultCachePolicy; 082 private final AtomicInteger retryLimit = new AtomicInteger(DEFAULT_RETRY_LIMIT); 083 private transient InMemoryRepository repository; 084 private transient RepositoryContext repositoryContext; 085 086 /** 087 * Create a repository source instance. 088 */ 089 public InMemoryRepositorySource() { 090 super(); 091 } 092 093 /** 094 * {@inheritDoc} 095 * 096 * @see org.jboss.dna.graph.connectors.RepositorySource#initialize(org.jboss.dna.graph.connectors.RepositoryContext) 097 */ 098 public void initialize( RepositoryContext context ) throws RepositorySourceException { 099 this.repositoryContext = context; 100 } 101 102 /** 103 * @return repositoryContext 104 */ 105 public RepositoryContext getRepositoryContext() { 106 return repositoryContext; 107 } 108 109 /** 110 * {@inheritDoc} 111 * 112 * @see org.jboss.dna.graph.connectors.RepositorySource#getRetryLimit() 113 */ 114 public int getRetryLimit() { 115 return retryLimit.get(); 116 } 117 118 /** 119 * {@inheritDoc} 120 * 121 * @see org.jboss.dna.graph.connectors.RepositorySource#setRetryLimit(int) 122 */ 123 public void setRetryLimit( int limit ) { 124 retryLimit.set(limit < 0 ? 0 : limit); 125 } 126 127 /** 128 * Get the default cache policy for this source, or null if the global default cache policy should be used 129 * 130 * @return the default cache policy, or null if this source has no explicit default cache policy 131 */ 132 public CachePolicy getDefaultCachePolicy() { 133 return defaultCachePolicy; 134 } 135 136 /** 137 * @param defaultCachePolicy Sets defaultCachePolicy to the specified value. 138 */ 139 public void setDefaultCachePolicy( CachePolicy defaultCachePolicy ) { 140 this.defaultCachePolicy = defaultCachePolicy; 141 } 142 143 /** 144 * @return rootNodeUuid 145 */ 146 public UUID getRootNodeUuid() { 147 return this.rootNodeUuid; 148 } 149 150 /** 151 * @param rootNodeUuid Sets rootNodeUuid to the specified value. 152 */ 153 public void setRootNodeUuid( UUID rootNodeUuid ) { 154 this.rootNodeUuid = rootNodeUuid != null ? rootNodeUuid : UUID.randomUUID(); 155 } 156 157 /** 158 * If you use this to set a JNDI name, this source will be bound to that name using the default {@link InitialContext}. You 159 * can also do this manually if you have additional requirements. 160 * 161 * @param name the JNDI name 162 * @throws NamingException if there is a problem registering this object 163 * @see #getJndiName() 164 */ 165 public void setJndiName( String name ) throws NamingException { 166 setJndiName(name, null); 167 } 168 169 /** 170 * Register this source in JNDI under the supplied name using the supplied context. to set a JNDI name, this source will be 171 * bound to that name using the default {@link InitialContext}. You can also do this manually if you have additional 172 * requirements. 173 * 174 * @param name the JNDI name, or null if this object is to no longer be registered 175 * @param context the JNDI context, or null if the {@link InitialContext} should be used 176 * @throws NamingException if there is a problem registering this object 177 * @see #getJndiName() 178 */ 179 public synchronized void setJndiName( String name, 180 Context context ) throws NamingException { 181 CheckArg.isNotNull(name, "name"); 182 if (context == null) context = new InitialContext(); 183 184 // First register in JNDI under the new name ... 185 if (name != null) { 186 context.bind(name, this); 187 } 188 // Second, unregister from JNDI if there is already a name ... 189 if (jndiName != null && !jndiName.equals(name)) { 190 context.unbind(jndiName); 191 } 192 // Record the new name ... 193 this.jndiName = name; 194 } 195 196 /** 197 * Gets the JNDI name this source is bound to. Only valid if you used setJNDIName to bind it. 198 * 199 * @return the JNDI name, or null if it is not bound in JNDI 200 * @see #setJndiName(String) 201 */ 202 public synchronized String getJndiName() { 203 return jndiName; 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 public String getName() { 210 return this.name; 211 } 212 213 /** 214 * @param name Sets name to the specified value. 215 */ 216 public void setName( String name ) { 217 this.name = name; 218 } 219 220 /** 221 * {@inheritDoc} 222 * 223 * @see org.jboss.dna.graph.connectors.RepositorySource#getConnection() 224 */ 225 public RepositoryConnection getConnection() throws RepositorySourceException { 226 if (repository == null) { 227 repository = new InMemoryRepository(name, rootNodeUuid); 228 } 229 return new InMemoryRepositoryConnection(this, repository); 230 } 231 232 /** 233 * {@inheritDoc} 234 */ 235 public synchronized Reference getReference() { 236 String className = getClass().getName(); 237 String factoryClassName = this.getClass().getName(); 238 Reference ref = new Reference(className, factoryClassName, null); 239 240 if (getName() != null) { 241 ref.add(new StringRefAddr(SOURCE_NAME, getName())); 242 } 243 if (getRootNodeUuid() != null) { 244 ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuid().toString())); 245 } 246 if (getJndiName() != null) { 247 ref.add(new StringRefAddr(JNDI_NAME, getJndiName())); 248 } 249 if (getDefaultCachePolicy() != null) { 250 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 251 CachePolicy policy = getDefaultCachePolicy(); 252 try { 253 ObjectOutputStream oos = new ObjectOutputStream(baos); 254 oos.writeObject(policy); 255 ref.add(new BinaryRefAddr(DEFAULT_CACHE_POLICY, baos.toByteArray())); 256 } catch (IOException e) { 257 I18n msg = InMemoryConnectorI18n.errorSerializingCachePolicyInSource; 258 throw new RepositorySourceException(getName(), msg.text(policy.getClass().getName(), getName()), e); 259 } 260 } 261 ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit()))); 262 return ref; 263 } 264 265 /** 266 * {@inheritDoc} 267 */ 268 public Object getObjectInstance( Object obj, 269 javax.naming.Name name, 270 Context nameCtx, 271 Hashtable<?, ?> environment ) throws Exception { 272 if (obj instanceof Reference) { 273 Map<String, Object> values = new HashMap<String, Object>(); 274 Reference ref = (Reference)obj; 275 Enumeration<?> en = ref.getAll(); 276 while (en.hasMoreElements()) { 277 RefAddr subref = (RefAddr)en.nextElement(); 278 if (subref instanceof StringRefAddr) { 279 String key = subref.getType(); 280 Object value = subref.getContent(); 281 if (value != null) values.put(key, value.toString()); 282 } else if (subref instanceof BinaryRefAddr) { 283 String key = subref.getType(); 284 Object value = subref.getContent(); 285 if (value instanceof byte[]) { 286 // Deserialize ... 287 ByteArrayInputStream bais = new ByteArrayInputStream((byte[])value); 288 ObjectInputStream ois = new ObjectInputStream(bais); 289 value = ois.readObject(); 290 values.put(key, value); 291 } 292 } 293 } 294 String sourceName = (String)values.get(SOURCE_NAME); 295 String rootNodeUuidString = (String)values.get(ROOT_NODE_UUID); 296 String jndiName = (String)values.get(JNDI_NAME); 297 Object defaultCachePolicy = values.get(DEFAULT_CACHE_POLICY); 298 String retryLimit = (String)values.get(RETRY_LIMIT); 299 300 // Create the source instance ... 301 InMemoryRepositorySource source = new InMemoryRepositorySource(); 302 if (sourceName != null) source.setName(sourceName); 303 if (rootNodeUuidString != null) source.setRootNodeUuid(UUID.fromString(rootNodeUuidString)); 304 if (jndiName != null) source.setJndiName(jndiName); 305 if (defaultCachePolicy instanceof CachePolicy) { 306 source.setDefaultCachePolicy((CachePolicy)defaultCachePolicy); 307 } 308 if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit)); 309 return source; 310 } 311 return null; 312 } 313 314 /** 315 * {@inheritDoc} 316 * 317 * @see org.jboss.dna.graph.connectors.RepositorySource#getCapabilities() 318 */ 319 public RepositorySourceCapabilities getCapabilities() { 320 return CAPABILITIES; 321 } 322 }