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