1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.graph.connector.inmemory;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.util.Enumeration;
32 import java.util.HashMap;
33 import java.util.Hashtable;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.UUID;
37 import java.util.concurrent.atomic.AtomicInteger;
38 import javax.naming.BinaryRefAddr;
39 import javax.naming.Context;
40 import javax.naming.InitialContext;
41 import javax.naming.NamingException;
42 import javax.naming.RefAddr;
43 import javax.naming.Reference;
44 import javax.naming.StringRefAddr;
45 import javax.naming.spi.ObjectFactory;
46 import net.jcip.annotations.GuardedBy;
47 import net.jcip.annotations.ThreadSafe;
48 import org.modeshape.common.i18n.I18n;
49 import org.modeshape.common.util.CheckArg;
50 import org.modeshape.common.util.StringUtil;
51 import org.modeshape.graph.ExecutionContext;
52 import org.modeshape.graph.GraphI18n;
53 import org.modeshape.graph.Subgraph;
54 import org.modeshape.graph.cache.CachePolicy;
55 import org.modeshape.graph.connector.RepositoryConnection;
56 import org.modeshape.graph.connector.RepositoryConnectionFactory;
57 import org.modeshape.graph.connector.RepositoryContext;
58 import org.modeshape.graph.connector.RepositorySource;
59 import org.modeshape.graph.connector.RepositorySourceCapabilities;
60 import org.modeshape.graph.connector.RepositorySourceException;
61 import org.modeshape.graph.connector.base.BaseRepositorySource;
62 import org.modeshape.graph.connector.base.Connection;
63 import org.modeshape.graph.observe.Observer;
64 import org.modeshape.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
65
66
67
68
69
70 @ThreadSafe
71 public class InMemoryRepositorySource implements BaseRepositorySource, ObjectFactory {
72
73
74
75
76 private static final long serialVersionUID = 1L;
77
78
79
80
81 public static final int DEFAULT_RETRY_LIMIT = 0;
82
83
84
85
86 public static final String DEFAULT_WORKSPACE_NAME = "";
87
88 protected static final RepositorySourceCapabilities CAPABILITIES = new RepositorySourceCapabilities(true, true, false, true,
89 true);
90
91 protected static final String ROOT_NODE_UUID_ATTR = "rootNodeUuid";
92 protected static final String SOURCE_NAME_ATTR = "sourceName";
93 protected static final String PREDEFINED_WORKSPACE_NAMES = "predefinedWorkspaceNames";
94 protected static final String DEFAULT_WORKSPACE_NAME_ATTR = "defaultWorkspaceName";
95 protected static final String DEFAULT_CACHE_POLICY_ATTR = "defaultCachePolicy";
96 protected static final String JNDI_NAME_ATTR = "jndiName";
97 protected static final String RETRY_LIMIT_ATTR = "retryLimit";
98
99 @GuardedBy( "sourcesLock" )
100 private String name;
101 @GuardedBy( "this" )
102 private String jndiName;
103 private String defaultWorkspaceName = DEFAULT_WORKSPACE_NAME;
104 private UUID rootNodeUuid = UUID.randomUUID();
105 private CachePolicy defaultCachePolicy;
106 private volatile String[] predefinedWorkspaces = new String[] {};
107 private final AtomicInteger retryLimit = new AtomicInteger(DEFAULT_RETRY_LIMIT);
108 private transient InMemoryRepository repository;
109 private transient ExecutionContext defaultContext = new ExecutionContext();
110 private transient RepositoryContext repositoryContext = new DefaultRepositoryContext();
111
112 protected class DefaultRepositoryContext implements RepositoryContext {
113
114
115
116
117
118 @SuppressWarnings( "synthetic-access" )
119 public ExecutionContext getExecutionContext() {
120 return defaultContext;
121 }
122
123
124
125
126
127
128 public Subgraph getConfiguration( int depth ) {
129 return null;
130 }
131
132
133
134
135
136
137 public Observer getObserver() {
138 return null;
139 }
140
141
142
143
144
145
146 public RepositoryConnectionFactory getRepositoryConnectionFactory() {
147 return null;
148 }
149 }
150
151
152
153
154 public InMemoryRepositorySource() {
155 super();
156 }
157
158
159
160
161
162
163 public void initialize( RepositoryContext context ) throws RepositorySourceException {
164 this.repositoryContext = context != null ? context : new DefaultRepositoryContext();
165 }
166
167
168
169
170 public RepositoryContext getRepositoryContext() {
171 return repositoryContext;
172 }
173
174
175
176
177
178
179 public int getRetryLimit() {
180 return retryLimit.get();
181 }
182
183
184
185
186
187
188 public void setRetryLimit( int limit ) {
189 retryLimit.set(limit < 0 ? 0 : limit);
190 }
191
192
193
194
195
196
197 public CachePolicy getDefaultCachePolicy() {
198 return defaultCachePolicy;
199 }
200
201
202
203
204 public void setDefaultCachePolicy( CachePolicy defaultCachePolicy ) {
205 this.defaultCachePolicy = defaultCachePolicy;
206 }
207
208
209
210
211
212
213 public String getDefaultWorkspaceName() {
214 return defaultWorkspaceName;
215 }
216
217
218
219
220
221
222 public void setDefaultWorkspaceName( String defaultWorkspaceName ) {
223 this.defaultWorkspaceName = defaultWorkspaceName != null ? defaultWorkspaceName : DEFAULT_WORKSPACE_NAME;
224 }
225
226
227
228
229
230
231 public String getRootNodeUuid() {
232 return this.rootNodeUuid.toString();
233 }
234
235
236
237
238
239
240 public UUID getRootNodeUuidObject() {
241 return this.rootNodeUuid;
242 }
243
244
245
246
247 public void setRootNodeUuid( UUID rootNodeUuid ) {
248 this.rootNodeUuid = rootNodeUuid != null ? rootNodeUuid : UUID.randomUUID();
249 }
250
251
252
253
254 public void setRootNodeUuid( String rootNodeUuid ) {
255 this.rootNodeUuid = rootNodeUuid != null ? UUID.fromString(rootNodeUuid) : UUID.randomUUID();
256 }
257
258
259
260
261
262
263
264
265
266 public void setJndiName( String name ) throws NamingException {
267 setJndiName(name, null);
268 }
269
270
271
272
273
274
275
276
277
278
279
280 public synchronized void setJndiName( String name,
281 Context context ) throws NamingException {
282 CheckArg.isNotNull(name, "name");
283 if (context == null) context = new InitialContext();
284
285
286 if (name != null) {
287 context.bind(name, this);
288 }
289
290 if (jndiName != null && !jndiName.equals(name)) {
291 context.unbind(jndiName);
292 }
293
294 this.jndiName = name;
295 }
296
297
298
299
300
301
302
303 public synchronized String getJndiName() {
304 return jndiName;
305 }
306
307
308
309
310 public String getName() {
311 return this.name;
312 }
313
314
315
316
317 public void setName( String name ) {
318 this.name = name;
319 }
320
321
322
323
324
325
326 public synchronized RepositoryConnection getConnection() throws RepositorySourceException {
327 if (repository == null) {
328 repository = new InMemoryRepository(this);
329
330 ExecutionContext context = repositoryContext != null ? repositoryContext.getExecutionContext() : defaultContext;
331 InMemoryTransaction txn = repository.startTransaction(context, false);
332 try {
333
334 for (String initialName : getPredefinedWorkspaceNames()) {
335 repository.createWorkspace(txn, initialName, CreateConflictBehavior.DO_NOT_CREATE, null);
336 }
337 } finally {
338 txn.commit();
339 }
340
341 }
342 return new Connection<InMemoryNode, InMemoryWorkspace>(this, repository);
343 }
344
345
346
347
348
349
350 public synchronized void close() {
351
352 this.repository = null;
353 }
354
355
356
357
358 public synchronized Reference getReference() {
359 String className = getClass().getName();
360 String factoryClassName = this.getClass().getName();
361 Reference ref = new Reference(className, factoryClassName, null);
362
363 if (getName() != null) {
364 ref.add(new StringRefAddr(SOURCE_NAME_ATTR, getName()));
365 }
366 if (getRootNodeUuid() != null) {
367 ref.add(new StringRefAddr(ROOT_NODE_UUID_ATTR, getRootNodeUuid().toString()));
368 }
369 if (getJndiName() != null) {
370 ref.add(new StringRefAddr(JNDI_NAME_ATTR, getJndiName()));
371 }
372 if (getDefaultWorkspaceName() != null) {
373 ref.add(new StringRefAddr(DEFAULT_WORKSPACE_NAME_ATTR, getDefaultWorkspaceName()));
374 }
375 String[] workspaceNames = getPredefinedWorkspaceNames();
376 if (workspaceNames != null && workspaceNames.length != 0) {
377 ref.add(new StringRefAddr(PREDEFINED_WORKSPACE_NAMES, StringUtil.combineLines(workspaceNames)));
378 }
379 if (getDefaultCachePolicy() != null) {
380 ByteArrayOutputStream baos = new ByteArrayOutputStream();
381 CachePolicy policy = getDefaultCachePolicy();
382 try {
383 ObjectOutputStream oos = new ObjectOutputStream(baos);
384 oos.writeObject(policy);
385 ref.add(new BinaryRefAddr(DEFAULT_CACHE_POLICY_ATTR, baos.toByteArray()));
386 } catch (IOException e) {
387 I18n msg = GraphI18n.errorSerializingInMemoryCachePolicyInSource;
388 throw new RepositorySourceException(getName(), msg.text(policy.getClass().getName(), getName()), e);
389 }
390 }
391 ref.add(new StringRefAddr(RETRY_LIMIT_ATTR, Integer.toString(getRetryLimit())));
392 return ref;
393 }
394
395
396
397
398 public Object getObjectInstance( Object obj,
399 javax.naming.Name name,
400 Context nameCtx,
401 Hashtable<?, ?> environment ) throws Exception {
402 if (obj instanceof Reference) {
403 Map<String, Object> values = new HashMap<String, Object>();
404 Reference ref = (Reference)obj;
405 Enumeration<?> en = ref.getAll();
406 while (en.hasMoreElements()) {
407 RefAddr subref = (RefAddr)en.nextElement();
408 if (subref instanceof StringRefAddr) {
409 String key = subref.getType();
410 Object value = subref.getContent();
411 if (value != null) values.put(key, value.toString());
412 } else if (subref instanceof BinaryRefAddr) {
413 String key = subref.getType();
414 Object value = subref.getContent();
415 if (value instanceof byte[]) {
416
417 ByteArrayInputStream bais = new ByteArrayInputStream((byte[])value);
418 ObjectInputStream ois = new ObjectInputStream(bais);
419 value = ois.readObject();
420 values.put(key, value);
421 }
422 }
423 }
424 String sourceName = (String)values.get(SOURCE_NAME_ATTR);
425 String rootNodeUuidString = (String)values.get(ROOT_NODE_UUID_ATTR);
426 String jndiName = (String)values.get(JNDI_NAME_ATTR);
427 String defaultWorkspaceName = (String)values.get(DEFAULT_WORKSPACE_NAME_ATTR);
428 Object defaultCachePolicy = values.get(DEFAULT_CACHE_POLICY_ATTR);
429 String retryLimit = (String)values.get(RETRY_LIMIT_ATTR);
430
431 String combinedWorkspaceNames = (String)values.get(PREDEFINED_WORKSPACE_NAMES);
432 String[] workspaceNames = null;
433 if (combinedWorkspaceNames != null) {
434 List<String> paths = StringUtil.splitLines(combinedWorkspaceNames);
435 workspaceNames = paths.toArray(new String[paths.size()]);
436 }
437
438
439 InMemoryRepositorySource source = new InMemoryRepositorySource();
440 if (sourceName != null) source.setName(sourceName);
441 if (rootNodeUuidString != null) source.setRootNodeUuid(UUID.fromString(rootNodeUuidString));
442 if (defaultWorkspaceName != null) source.setDefaultWorkspaceName(defaultWorkspaceName);
443 if (jndiName != null) source.setJndiName(jndiName);
444 if (defaultCachePolicy instanceof CachePolicy) {
445 source.setDefaultCachePolicy((CachePolicy)defaultCachePolicy);
446 }
447 if (workspaceNames != null && workspaceNames.length != 0) source.setPredefinedWorkspaceNames(workspaceNames);
448 if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
449 return source;
450 }
451 return null;
452 }
453
454
455
456
457
458
459
460 public synchronized String[] getPredefinedWorkspaceNames() {
461 String[] copy = new String[predefinedWorkspaces.length];
462 System.arraycopy(predefinedWorkspaces, 0, copy, 0, predefinedWorkspaces.length);
463 return copy;
464 }
465
466
467
468
469
470
471
472
473 public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspaceNames ) {
474 this.predefinedWorkspaces = predefinedWorkspaceNames;
475 }
476
477
478
479
480
481
482 public RepositorySourceCapabilities getCapabilities() {
483 return CAPABILITIES;
484 }
485
486 public boolean areUpdatesAllowed() {
487 return true;
488 }
489
490
491
492
493
494
495
496
497
498 public void setUpdatesAllowed( boolean updatesAllowed ) {
499 if (updatesAllowed == false) {
500 throw new RepositorySourceException(GraphI18n.inMemoryConnectorMustAllowUpdates.text(this.name));
501 }
502
503 }
504
505
506
507
508
509
510 @Override
511 public String toString() {
512 return "The \"" + name + "\" in-memory repository";
513 }
514 }