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.connector.jbosscache;
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.HashSet;
034    import java.util.Hashtable;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.Set;
038    import java.util.UUID;
039    import javax.naming.BinaryRefAddr;
040    import javax.naming.Context;
041    import javax.naming.InitialContext;
042    import javax.naming.NamingException;
043    import javax.naming.RefAddr;
044    import javax.naming.Reference;
045    import javax.naming.Referenceable;
046    import javax.naming.StringRefAddr;
047    import javax.naming.spi.ObjectFactory;
048    import net.jcip.annotations.ThreadSafe;
049    import org.jboss.cache.Cache;
050    import org.jboss.cache.CacheFactory;
051    import org.jboss.cache.DefaultCacheFactory;
052    import org.jboss.dna.common.i18n.I18n;
053    import org.jboss.dna.common.util.StringUtil;
054    import org.jboss.dna.graph.DnaLexicon;
055    import org.jboss.dna.graph.cache.CachePolicy;
056    import org.jboss.dna.graph.connector.RepositoryConnection;
057    import org.jboss.dna.graph.connector.RepositoryContext;
058    import org.jboss.dna.graph.connector.RepositorySource;
059    import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
060    import org.jboss.dna.graph.connector.RepositorySourceException;
061    import org.jboss.dna.graph.property.Name;
062    
063    /**
064     * A repository source that uses a JBoss Cache instance to manage the content. This source is capable of using an existing
065     * {@link Cache} instance or creating a new instance. This process is controlled entirely by the JavaBean properties of the
066     * JBossCacheSource instance.
067     * <p>
068     * This source first attempts to find an existing cache in {@link #getCacheJndiName() JNDI}. If none is found, then it attempts to
069     * create a cache instance using the {@link CacheFactory} found in {@link #getCacheFactoryJndiName() JNDI} (or the
070     * {@link DefaultCacheFactory} if no such factory is available) and the {@link #getCacheConfigurationName() cache configuration
071     * name} if supplied or the default configuration if not set.
072     * </p>
073     * <p>
074     * Like other {@link RepositorySource} classes, instances of JBossCacheSource can be placed into JNDI and do support the creation
075     * of {@link Referenceable JNDI referenceable} objects and resolution of references into JBossCacheSource.
076     * </p>
077     * 
078     * @author Randall Hauch
079     */
080    @ThreadSafe
081    public class JBossCacheSource implements RepositorySource, ObjectFactory {
082    
083        private static final long serialVersionUID = 2L;
084        /**
085         * The default limit is {@value} for retrying {@link RepositoryConnection connection} calls to the underlying source.
086         */
087        public static final int DEFAULT_RETRY_LIMIT = 0;
088        public static final String DEFAULT_UUID_PROPERTY_NAME = DnaLexicon.UUID.getString();
089    
090        /**
091         * The initial {@link #getNameOfDefaultWorkspace() name of the default workspace} is "{@value} ", unless otherwise specified.
092         */
093        public static final String DEFAULT_NAME_OF_DEFAULT_WORKSPACE = "default";
094    
095        protected static final String ROOT_NODE_UUID = "rootNodeUuid";
096        protected static final String SOURCE_NAME = "sourceName";
097        protected static final String DEFAULT_CACHE_POLICY = "defaultCachePolicy";
098        protected static final String CACHE_CONFIGURATION_NAME = "cacheConfigurationName";
099        protected static final String CACHE_FACTORY_JNDI_NAME = "cacheFactoryJndiName";
100        protected static final String CACHE_JNDI_NAME = "cacheJndiName";
101        protected static final String RETRY_LIMIT = "retryLimit";
102        protected static final String DEFAULT_WORKSPACE = "defaultWorkspace";
103        protected static final String PREDEFINED_WORKSPACE_NAMES = "predefinedWorkspaceNames";
104        protected static final String ALLOW_CREATING_WORKSPACES = "allowCreatingWorkspaces";
105    
106        private volatile String name;
107        private volatile UUID rootNodeUuid = UUID.randomUUID();
108        private volatile CachePolicy defaultCachePolicy;
109        private volatile String cacheConfigurationName;
110        private volatile String cacheFactoryJndiName;
111        private volatile String cacheJndiName;
112        private volatile int retryLimit = DEFAULT_RETRY_LIMIT;
113        private volatile String defaultWorkspace;
114        private volatile String[] predefinedWorkspaces = new String[] {};
115        private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(true, true, false, true, false);
116        private transient JBossCacheWorkspaces workspaces;
117        private transient Context jndiContext;
118    
119        /**
120         * Create a repository source instance.
121         */
122        public JBossCacheSource() {
123        }
124    
125        /**
126         * {@inheritDoc}
127         * 
128         * @see org.jboss.dna.graph.connector.RepositorySource#initialize(org.jboss.dna.graph.connector.RepositoryContext)
129         */
130        public void initialize( RepositoryContext context ) throws RepositorySourceException {
131        }
132    
133        /**
134         * {@inheritDoc}
135         */
136        public String getName() {
137            return this.name;
138        }
139    
140        /**
141         * {@inheritDoc}
142         * 
143         * @see org.jboss.dna.graph.connector.RepositorySource#getCapabilities()
144         */
145        public RepositorySourceCapabilities getCapabilities() {
146            return capabilities;
147        }
148    
149        /**
150         * {@inheritDoc}
151         * 
152         * @see org.jboss.dna.graph.connector.RepositorySource#getRetryLimit()
153         */
154        public int getRetryLimit() {
155            return retryLimit;
156        }
157    
158        /**
159         * {@inheritDoc}
160         * 
161         * @see org.jboss.dna.graph.connector.RepositorySource#setRetryLimit(int)
162         */
163        public synchronized void setRetryLimit( int limit ) {
164            retryLimit = limit < 0 ? 0 : limit;
165        }
166    
167        /**
168         * Set the name of this source
169         * 
170         * @param name the name for this source
171         */
172        public synchronized void setName( String name ) {
173            if (this.name == name || this.name != null && this.name.equals(name)) return; // unchanged
174            this.name = name;
175        }
176    
177        /**
178         * Get the default cache policy for this source, or null if the global default cache policy should be used
179         * 
180         * @return the default cache policy, or null if this source has no explicit default cache policy
181         */
182        public CachePolicy getDefaultCachePolicy() {
183            return defaultCachePolicy;
184        }
185    
186        /**
187         * @param defaultCachePolicy Sets defaultCachePolicy to the specified value.
188         */
189        public synchronized void setDefaultCachePolicy( CachePolicy defaultCachePolicy ) {
190            if (this.defaultCachePolicy == defaultCachePolicy || this.defaultCachePolicy != null
191                && this.defaultCachePolicy.equals(defaultCachePolicy)) return; // unchanged
192            this.defaultCachePolicy = defaultCachePolicy;
193        }
194    
195        /**
196         * Get the name in JNDI of a {@link Cache} instance that should be used by this source.
197         * <p>
198         * This source first attempts to find an existing cache in {@link #getCacheJndiName() JNDI}. If none is found, then it
199         * attempts to create a cache instance using the {@link CacheFactory} found in {@link #getCacheFactoryJndiName() JNDI} (or the
200         * {@link DefaultCacheFactory} if no such factory is available) and the {@link #getCacheConfigurationName() cache
201         * configuration name} if supplied or the default configuration if not set.
202         * </p>
203         * 
204         * @return the JNDI name of the {@link Cache} instance that should be used, or null if the cache is to be created with a cache
205         *         factory {@link #getCacheFactoryJndiName() found in JNDI} using the specified {@link #getCacheConfigurationName()
206         *         cache configuration name}.
207         * @see #setCacheJndiName(String)
208         * @see #getCacheConfigurationName()
209         * @see #getCacheFactoryJndiName()
210         */
211        public String getCacheJndiName() {
212            return cacheJndiName;
213        }
214    
215        /**
216         * Set the name in JNDI of a {@link Cache} instance that should be used by this source.
217         * <p>
218         * This source first attempts to find an existing cache in {@link #getCacheJndiName() JNDI}. If none is found, then it
219         * attempts to create a cache instance using the {@link CacheFactory} found in {@link #getCacheFactoryJndiName() JNDI} (or the
220         * {@link DefaultCacheFactory} if no such factory is available) and the {@link #getCacheConfigurationName() cache
221         * configuration name} if supplied or the default configuration if not set.
222         * </p>
223         * 
224         * @param cacheJndiName the JNDI name of the {@link Cache} instance that should be used, or null if the cache is to be created
225         *        with a cache factory {@link #getCacheFactoryJndiName() found in JNDI} using the specified
226         *        {@link #getCacheConfigurationName() cache configuration name}.
227         * @see #getCacheJndiName()
228         * @see #getCacheConfigurationName()
229         * @see #getCacheFactoryJndiName()
230         */
231        public synchronized void setCacheJndiName( String cacheJndiName ) {
232            if (this.cacheJndiName == cacheJndiName || this.cacheJndiName != null && this.cacheJndiName.equals(cacheJndiName)) return; // unchanged
233            this.cacheJndiName = cacheJndiName;
234        }
235    
236        /**
237         * Get the name in JNDI of a {@link CacheFactory} instance that should be used to create the cache for this source.
238         * <p>
239         * This source first attempts to find an existing cache in {@link #getCacheJndiName() JNDI}. If none is found, then it
240         * attempts to create a cache instance using the {@link CacheFactory} found in {@link #getCacheFactoryJndiName() JNDI} (or the
241         * {@link DefaultCacheFactory} if no such factory is available) and the {@link #getCacheConfigurationName() cache
242         * configuration name} if supplied or the default configuration if not set.
243         * </p>
244         * 
245         * @return the JNDI name of the {@link CacheFactory} instance that should be used, or null if the {@link DefaultCacheFactory}
246         *         should be used if a cache is to be created
247         * @see #setCacheFactoryJndiName(String)
248         * @see #getCacheConfigurationName()
249         * @see #getCacheJndiName()
250         */
251        public String getCacheFactoryJndiName() {
252            return cacheFactoryJndiName;
253        }
254    
255        /**
256         * Set the name in JNDI of a {@link CacheFactory} instance that should be used to obtain the {@link Cache} instance used by
257         * this source.
258         * <p>
259         * This source first attempts to find an existing cache in {@link #getCacheJndiName() JNDI}. If none is found, then it
260         * attempts to create a cache instance using the {@link CacheFactory} found in {@link #getCacheFactoryJndiName() JNDI} (or the
261         * {@link DefaultCacheFactory} if no such factory is available) and the {@link #getCacheConfigurationName() cache
262         * configuration name} if supplied or the default configuration if not set.
263         * </p>
264         * 
265         * @param jndiName the JNDI name of the {@link CacheFactory} instance that should be used, or null if the
266         *        {@link DefaultCacheFactory} should be used if a cache is to be created
267         * @see #setCacheFactoryJndiName(String)
268         * @see #getCacheConfigurationName()
269         * @see #getCacheJndiName()
270         */
271        public synchronized void setCacheFactoryJndiName( String jndiName ) {
272            if (this.cacheFactoryJndiName == jndiName || this.cacheFactoryJndiName != null
273                && this.cacheFactoryJndiName.equals(jndiName)) return; // unchanged
274            this.cacheFactoryJndiName = jndiName;
275        }
276    
277        /**
278         * Get the name of the configuration that should be used if a {@link Cache cache} is to be created using the
279         * {@link CacheFactory} found in JNDI or the {@link DefaultCacheFactory} if needed.
280         * <p>
281         * This source first attempts to find an existing cache in {@link #getCacheJndiName() JNDI}. If none is found, then it
282         * attempts to create a cache instance using the {@link CacheFactory} found in {@link #getCacheFactoryJndiName() JNDI} (or the
283         * {@link DefaultCacheFactory} if no such factory is available) and the {@link #getCacheConfigurationName() cache
284         * configuration name} if supplied or the default configuration if not set.
285         * </p>
286         * 
287         * @return the name of the configuration that should be passed to the {@link CacheFactory}, or null if the default
288         *         configuration should be used
289         * @see #setCacheConfigurationName(String)
290         * @see #getCacheFactoryJndiName()
291         * @see #getCacheJndiName()
292         */
293        public String getCacheConfigurationName() {
294            return cacheConfigurationName;
295        }
296    
297        /**
298         * Get the name of the configuration that should be used if a {@link Cache cache} is to be created using the
299         * {@link CacheFactory} found in JNDI or the {@link DefaultCacheFactory} if needed.
300         * <p>
301         * This source first attempts to find an existing cache in {@link #getCacheJndiName() JNDI}. If none is found, then it
302         * attempts to create a cache instance using the {@link CacheFactory} found in {@link #getCacheFactoryJndiName() JNDI} (or the
303         * {@link DefaultCacheFactory} if no such factory is available) and the {@link #getCacheConfigurationName() cache
304         * configuration name} if supplied or the default configuration if not set.
305         * </p>
306         * 
307         * @param cacheConfigurationName the name of the configuration that should be passed to the {@link CacheFactory}, or null if
308         *        the default configuration should be used
309         * @see #getCacheConfigurationName()
310         * @see #getCacheFactoryJndiName()
311         * @see #getCacheJndiName()
312         */
313        public synchronized void setCacheConfigurationName( String cacheConfigurationName ) {
314            if (this.cacheConfigurationName == cacheConfigurationName || this.cacheConfigurationName != null
315                && this.cacheConfigurationName.equals(cacheConfigurationName)) return; // unchanged
316            this.cacheConfigurationName = cacheConfigurationName;
317        }
318    
319        /**
320         * Get the UUID of the root node for the cache. If the cache exists, this UUID is not used but is instead set to the UUID of
321         * the existing root node.
322         * 
323         * @return the UUID of the root node for the cache.
324         */
325        public String getRootNodeUuid() {
326            return this.rootNodeUuid.toString();
327        }
328    
329        /**
330         * Get the UUID of the root node for the cache. If the cache exists, this UUID is not used but is instead set to the UUID of
331         * the existing root node.
332         * 
333         * @return the UUID of the root node for the cache.
334         */
335        public UUID getRootNodeUuidObject() {
336            return this.rootNodeUuid;
337        }
338    
339        /**
340         * Set the UUID of the root node in this repository. If the cache exists, this UUID is not used but is instead set to the UUID
341         * of the existing root node.
342         * 
343         * @param rootNodeUuid the UUID of the root node for the cache, or null if the UUID should be randomly generated
344         */
345        public synchronized void setRootNodeUuid( String rootNodeUuid ) {
346            UUID uuid = null;
347            if (rootNodeUuid == null) uuid = UUID.randomUUID();
348            else uuid = UUID.fromString(rootNodeUuid);
349            if (this.rootNodeUuid.equals(uuid)) return; // unchanged
350            this.rootNodeUuid = uuid;
351        }
352    
353        /**
354         * Get the name of the default workspace.
355         * 
356         * @return the name of the workspace that should be used by default; never null
357         */
358        public String getNameOfDefaultWorkspace() {
359            return defaultWorkspace;
360        }
361    
362        /**
363         * Set the name of the workspace that should be used when clients don't specify a workspace.
364         * 
365         * @param nameOfDefaultWorkspace the name of the workspace that should be used by default, or null if the
366         *        {@link #DEFAULT_NAME_OF_DEFAULT_WORKSPACE default name} should be used
367         */
368        public synchronized void setNameOfDefaultWorkspace( String nameOfDefaultWorkspace ) {
369            this.defaultWorkspace = nameOfDefaultWorkspace != null ? nameOfDefaultWorkspace : DEFAULT_NAME_OF_DEFAULT_WORKSPACE;
370        }
371    
372        /**
373         * Gets the names of the workspaces that are available when this source is created.
374         * 
375         * @return the names of the workspaces that this source starts with, or null if there are no such workspaces
376         * @see #setPredefinedWorkspaceNames(String[])
377         * @see #setCreatingWorkspacesAllowed(boolean)
378         */
379        public synchronized String[] getPredefinedWorkspaceNames() {
380            String[] copy = new String[predefinedWorkspaces.length];
381            System.arraycopy(predefinedWorkspaces, 0, copy, 0, predefinedWorkspaces.length);
382            return copy;
383        }
384    
385        /**
386         * Sets the names of the workspaces that are available when this source is created.
387         * 
388         * @param predefinedWorkspaceNames the names of the workspaces that this source should start with, or null if there are no
389         *        such workspaces
390         * @see #setCreatingWorkspacesAllowed(boolean)
391         * @see #getPredefinedWorkspaceNames()
392         */
393        public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspaceNames ) {
394            this.predefinedWorkspaces = predefinedWorkspaceNames;
395        }
396    
397        /**
398         * Get whether this source allows workspaces to be created dynamically.
399         * 
400         * @return true if this source allows workspaces to be created by clients, or false if the
401         *         {@link #getPredefinedWorkspaceNames() set of workspaces} is fixed
402         * @see #setPredefinedWorkspaceNames(String[])
403         * @see #getPredefinedWorkspaceNames()
404         * @see #setCreatingWorkspacesAllowed(boolean)
405         */
406        public boolean isCreatingWorkspacesAllowed() {
407            return capabilities.supportsCreatingWorkspaces();
408        }
409    
410        /**
411         * Set whether this source allows workspaces to be created dynamically.
412         * 
413         * @param allowWorkspaceCreation true if this source allows workspaces to be created by clients, or false if the
414         *        {@link #getPredefinedWorkspaceNames() set of workspaces} is fixed
415         * @see #setPredefinedWorkspaceNames(String[])
416         * @see #getPredefinedWorkspaceNames()
417         * @see #isCreatingWorkspacesAllowed()
418         */
419        public synchronized void setCreatingWorkspacesAllowed( boolean allowWorkspaceCreation ) {
420            capabilities = new RepositorySourceCapabilities(true, capabilities.supportsUpdates(), false, allowWorkspaceCreation,
421                                                            capabilities.supportsReferences());
422        }
423    
424        /**
425         * {@inheritDoc}
426         * 
427         * @see org.jboss.dna.graph.connector.RepositorySource#getConnection()
428         */
429        @SuppressWarnings( "unchecked" )
430        public synchronized RepositoryConnection getConnection() throws RepositorySourceException {
431            if (getName() == null) {
432                I18n msg = JBossCacheConnectorI18n.propertyIsRequired;
433                throw new RepositorySourceException(getName(), msg.text("name"));
434            }
435            if (this.workspaces == null) {
436                Context context = getContext();
437                if (context == null) {
438                    try {
439                        context = new InitialContext();
440                    } catch (NamingException err) {
441                        throw new RepositorySourceException(name, err);
442                    }
443                }
444    
445                // Look for a cache factory in JNDI ...
446                CacheFactory<Name, Object> cacheFactory = null;
447                String jndiName = getCacheFactoryJndiName();
448                if (jndiName != null && jndiName.trim().length() != 0) {
449                    Object object = null;
450                    try {
451                        object = context.lookup(jndiName);
452                        if (object != null) cacheFactory = (CacheFactory<Name, Object>)object;
453                    } catch (ClassCastException err) {
454                        I18n msg = JBossCacheConnectorI18n.objectFoundInJndiWasNotCacheFactory;
455                        String className = object != null ? object.getClass().getName() : "null";
456                        throw new RepositorySourceException(getName(), msg.text(jndiName, this.getName(), className), err);
457                    } catch (Throwable err) {
458                        if (err instanceof RuntimeException) throw (RuntimeException)err;
459                        throw new RepositorySourceException(getName(), err);
460                    }
461                }
462                if (cacheFactory == null) cacheFactory = new DefaultCacheFactory<Name, Object>();
463    
464                // Get the default cache configuration name
465                String configName = this.getCacheConfigurationName();
466    
467                // Create the set of initial names ...
468                Set<String> initialNames = new HashSet<String>();
469                for (String initialName : getPredefinedWorkspaceNames())
470                    initialNames.add(initialName);
471    
472                // Now create the workspace manager ...
473                this.workspaces = new JBossCacheWorkspaces(getName(), cacheFactory, configName, initialNames, context);
474            }
475    
476            return new JBossCacheConnection(this, this.workspaces);
477        }
478    
479        protected Context getContext() {
480            return this.jndiContext;
481        }
482    
483        protected synchronized void setContext( Context context ) {
484            this.jndiContext = context;
485        }
486    
487        /**
488         * {@inheritDoc}
489         */
490        @Override
491        public boolean equals( Object obj ) {
492            if (obj == this) return true;
493            if (obj instanceof JBossCacheSource) {
494                JBossCacheSource that = (JBossCacheSource)obj;
495                if (this.getName() == null) {
496                    if (that.getName() != null) return false;
497                } else {
498                    if (!this.getName().equals(that.getName())) return false;
499                }
500                return true;
501            }
502            return false;
503        }
504    
505        /**
506         * {@inheritDoc}
507         */
508        public synchronized Reference getReference() {
509            String className = getClass().getName();
510            String factoryClassName = this.getClass().getName();
511            Reference ref = new Reference(className, factoryClassName, null);
512    
513            ref.add(new StringRefAddr(SOURCE_NAME, getName()));
514            ref.add(new StringRefAddr(ROOT_NODE_UUID, getRootNodeUuid().toString()));
515            ref.add(new StringRefAddr(CACHE_JNDI_NAME, getCacheJndiName()));
516            ref.add(new StringRefAddr(CACHE_FACTORY_JNDI_NAME, getCacheFactoryJndiName()));
517            ref.add(new StringRefAddr(CACHE_CONFIGURATION_NAME, getCacheConfigurationName()));
518            ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
519            ref.add(new StringRefAddr(DEFAULT_WORKSPACE, getNameOfDefaultWorkspace()));
520            ref.add(new StringRefAddr(ALLOW_CREATING_WORKSPACES, Boolean.toString(isCreatingWorkspacesAllowed())));
521            String[] workspaceNames = getPredefinedWorkspaceNames();
522            if (workspaceNames != null && workspaceNames.length != 0) {
523                ref.add(new StringRefAddr(PREDEFINED_WORKSPACE_NAMES, StringUtil.combineLines(workspaceNames)));
524            }
525            if (getDefaultCachePolicy() != null) {
526                ByteArrayOutputStream baos = new ByteArrayOutputStream();
527                CachePolicy policy = getDefaultCachePolicy();
528                try {
529                    ObjectOutputStream oos = new ObjectOutputStream(baos);
530                    oos.writeObject(policy);
531                    ref.add(new BinaryRefAddr(DEFAULT_CACHE_POLICY, baos.toByteArray()));
532                } catch (IOException e) {
533                    I18n msg = JBossCacheConnectorI18n.errorSerializingCachePolicyInSource;
534                    throw new RepositorySourceException(getName(), msg.text(policy.getClass().getName(), getName()), e);
535                }
536            }
537            return ref;
538        }
539    
540        /**
541         * {@inheritDoc}
542         */
543        public Object getObjectInstance( Object obj,
544                                         javax.naming.Name name,
545                                         Context nameCtx,
546                                         Hashtable<?, ?> environment ) throws Exception {
547            if (obj instanceof Reference) {
548                Map<String, Object> values = new HashMap<String, Object>();
549                Reference ref = (Reference)obj;
550                Enumeration<?> en = ref.getAll();
551                while (en.hasMoreElements()) {
552                    RefAddr subref = (RefAddr)en.nextElement();
553                    if (subref instanceof StringRefAddr) {
554                        String key = subref.getType();
555                        Object value = subref.getContent();
556                        if (value != null) values.put(key, value.toString());
557                    } else if (subref instanceof BinaryRefAddr) {
558                        String key = subref.getType();
559                        Object value = subref.getContent();
560                        if (value instanceof byte[]) {
561                            // Deserialize ...
562                            ByteArrayInputStream bais = new ByteArrayInputStream((byte[])value);
563                            ObjectInputStream ois = new ObjectInputStream(bais);
564                            value = ois.readObject();
565                            values.put(key, value);
566                        }
567                    }
568                }
569                String sourceName = (String)values.get(SOURCE_NAME);
570                String rootNodeUuidString = (String)values.get(ROOT_NODE_UUID);
571                String cacheJndiName = (String)values.get(CACHE_JNDI_NAME);
572                String cacheFactoryJndiName = (String)values.get(CACHE_FACTORY_JNDI_NAME);
573                String cacheConfigurationName = (String)values.get(CACHE_CONFIGURATION_NAME);
574                Object defaultCachePolicy = values.get(DEFAULT_CACHE_POLICY);
575                String retryLimit = (String)values.get(RETRY_LIMIT);
576                String defaultWorkspace = (String)values.get(DEFAULT_WORKSPACE);
577                String createWorkspaces = (String)values.get(ALLOW_CREATING_WORKSPACES);
578    
579                String combinedWorkspaceNames = (String)values.get(PREDEFINED_WORKSPACE_NAMES);
580                String[] workspaceNames = null;
581                if (combinedWorkspaceNames != null) {
582                    List<String> paths = StringUtil.splitLines(combinedWorkspaceNames);
583                    workspaceNames = paths.toArray(new String[paths.size()]);
584                }
585    
586                // Create the source instance ...
587                JBossCacheSource source = new JBossCacheSource();
588                if (sourceName != null) source.setName(sourceName);
589                if (rootNodeUuidString != null) source.setRootNodeUuid(rootNodeUuidString);
590                if (cacheJndiName != null) source.setCacheJndiName(cacheJndiName);
591                if (cacheFactoryJndiName != null) source.setCacheFactoryJndiName(cacheFactoryJndiName);
592                if (cacheConfigurationName != null) source.setCacheConfigurationName(cacheConfigurationName);
593                if (defaultCachePolicy instanceof CachePolicy) {
594                    source.setDefaultCachePolicy((CachePolicy)defaultCachePolicy);
595                }
596                if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
597                if (defaultWorkspace != null) source.setNameOfDefaultWorkspace(defaultWorkspace);
598                if (createWorkspaces != null) source.setCreatingWorkspacesAllowed(Boolean.parseBoolean(createWorkspaces));
599                if (workspaceNames != null && workspaceNames.length != 0) source.setPredefinedWorkspaceNames(workspaceNames);
600                return source;
601            }
602            return null;
603        }
604    }