View Javadoc

1   /*
2    * Copyright (c) 2007, James Leigh All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    * 
7    * - Redistributions of source code must retain the above copyright notice, this
8    *   list of conditions and the following disclaimer.
9    * - Redistributions in binary form must reproduce the above copyright notice,
10   *   this list of conditions and the following disclaimer in the documentation
11   *   and/or other materials provided with the distribution. 
12   * - Neither the name of the openrdf.org nor the names of its contributors may
13   *   be used to endorse or promote products derived from this software without
14   *   specific prior written permission.
15   * 
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26   * POSSIBILITY OF SUCH DAMAGE.
27   * 
28   */
29  package org.openrdf.elmo.sesame;
30  
31  import java.util.Collections;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.Locale;
35  import java.util.Set;
36  
37  import javax.persistence.EntityManager;
38  import javax.persistence.EntityTransaction;
39  import javax.persistence.FlushModeType;
40  import javax.persistence.LockModeType;
41  import javax.persistence.Query;
42  import javax.persistence.TransactionRequiredException;
43  import javax.xml.namespace.QName;
44  
45  import org.openrdf.elmo.ElmoEntityResolver;
46  import org.openrdf.elmo.ElmoManager;
47  import org.openrdf.elmo.Entity;
48  import org.openrdf.elmo.LiteralManager;
49  import org.openrdf.elmo.Memento;
50  import org.openrdf.elmo.Mergeable;
51  import org.openrdf.elmo.Refreshable;
52  import org.openrdf.elmo.ResourceManager;
53  import org.openrdf.elmo.RoleMapper;
54  import org.openrdf.elmo.exceptions.ElmoCompositionException;
55  import org.openrdf.elmo.exceptions.ElmoIOException;
56  import org.openrdf.elmo.exceptions.ElmoMementoException;
57  import org.openrdf.elmo.exceptions.ElmoPersistException;
58  import org.openrdf.elmo.sesame.iterators.ElmoIteration;
59  import org.openrdf.elmo.sesame.roles.SesameEntity;
60  import org.openrdf.elmo.sesame.roles.SesameManagerAware;
61  import org.openrdf.model.Literal;
62  import org.openrdf.model.Resource;
63  import org.openrdf.model.URI;
64  import org.openrdf.model.Value;
65  import org.openrdf.query.MalformedQueryException;
66  import org.openrdf.query.QueryLanguage;
67  import org.openrdf.query.TupleQuery;
68  import org.openrdf.repository.DelegatingRepositoryConnection;
69  import org.openrdf.repository.RepositoryConnection;
70  import org.openrdf.repository.RepositoryException;
71  import org.openrdf.repository.contextaware.ContextAwareConnection;
72  import org.openrdf.repository.flushable.FlushableConnection;
73  import org.slf4j.Logger;
74  import org.slf4j.LoggerFactory;
75  
76  /**
77   * Handles operations of ElmoManager and EntityManager.
78   * 
79   * @author James Leigh
80   * 
81   */
82  public class SesameManager implements ElmoManager, EntityManager {
83  
84  	final Logger logger = LoggerFactory.getLogger(SesameManager.class);
85  
86  	private ContextAwareConnection conn;
87  
88  	private SesameTransaction trans;
89  
90  	private Locale locale;
91  
92  	private String language;
93  
94  	private ResourceManager<Resource> resources;
95  
96  	private LiteralManager<URI, Literal> lm;
97  
98  	private ElmoEntityResolver resolver;
99  
100 	private RoleMapper<URI> mapper;
101 
102 	public ContextAwareConnection getConnection() {
103 		if (!isOpen())
104 			throw new IllegalStateException("Connection has been closed");
105 		return conn;
106 	}
107 
108 	public void setConnection(ContextAwareConnection connection) {
109 		this.conn = connection;
110 		this.trans = new SesameTransaction(conn);
111 	}
112 
113 	public EntityTransaction getTransaction() {
114 		return trans;
115 	}
116 
117 	public void joinTransaction() {
118 		if (!isOpen())
119 			throw new TransactionRequiredException();
120 	}
121 
122 	public Locale getLocale() {
123 		return locale;
124 	}
125 
126 	public void setLocale(Locale locale) {
127 		this.locale = locale;
128 		if (locale != null) {
129 			String lang = locale.toString().replace('_', '-');
130 			language = lang.toLowerCase();
131 		}
132 	}
133 
134 	public String getLanguage() {
135 		return language;
136 	}
137 
138 	public void setResourceManager(ResourceManager<Resource> manager) {
139 		this.resources = manager;
140 	}
141 
142 	public void setLiteralManager(LiteralManager<URI, Literal> manager) {
143 		this.lm = manager;
144 	}
145 
146 	public void setElmoClassResolver(ElmoEntityResolver resolver) {
147 		this.resolver = resolver;
148 	}
149 
150 	public void setRoleMapper(RoleMapper<URI> mapper) {
151 		this.mapper = mapper;
152 	}
153 
154 	public boolean isOpen() {
155 		try {
156 			return conn != null && conn.isOpen();
157 		} catch (RepositoryException e) {
158 			throw new ElmoIOException(e);
159 		}
160 	}
161 
162 	public void close() {
163 		try {
164 			if (conn != null && !trans.isActive())
165 				conn.close();
166 			conn = null;
167 		} catch (RepositoryException e) {
168 			throw new ElmoPersistException(e);
169 		}
170 	}
171 
172 	public void flush() {
173 		try {
174 			FlushableConnection flusher;
175 			flusher = findFlushableConnection(getConnection());
176 			if (flusher != null) {
177 				flusher.flush();
178 			}
179 		} catch (RepositoryException e) {
180 			throw new ElmoPersistException(e);
181 		}
182 	}
183 
184 	public void clear() {
185 		try {
186 			FlushableConnection flusher;
187 			flusher = findFlushableConnection(getConnection());
188 			if (flusher != null) {
189 				flusher.clear();
190 			}
191 		} catch (RepositoryException e) {
192 			throw new ElmoPersistException(e);
193 		}
194 	}
195 
196 	public FlushModeType getFlushMode() {
197 		try {
198 			FlushableConnection flusher;
199 			flusher = findFlushableConnection(getConnection());
200 			if (flusher == null || flusher.isAutoFlush())
201 				return FlushModeType.AUTO;
202 			return FlushModeType.COMMIT;
203 		} catch (RepositoryException e) {
204 			throw new ElmoIOException(e);
205 		}
206 	}
207 
208 	public void setFlushMode(FlushModeType mode) {
209 		try {
210 			FlushableConnection flusher;
211 			flusher = findFlushableConnection(getConnection());
212 			if (flusher != null) {
213 				flusher.setAutoFlush(mode.equals(FlushModeType.AUTO));
214 			}
215 		} catch (RepositoryException e) {
216 			throw new ElmoPersistException(e);
217 		}
218 	}
219 
220 	public Object getDelegate() {
221 		return this;
222 	}
223 
224 	public Memento createMemento() {
225 		try {
226 			return new SesameMemento(getConnection());
227 		} catch (RepositoryException e) {
228 			throw new ElmoMementoException(e);
229 		} catch (IllegalArgumentException e) {
230 			throw new UnsupportedOperationException(e);
231 		}
232 	}
233 
234 	public void undoMemento(Memento memento) {
235 		if (!(memento instanceof SesameMemento))
236 			throw new IllegalArgumentException();
237 		SesameMemento m = (SesameMemento) memento;
238 		try {
239 			m.undo(getConnection());
240 		} catch (RepositoryException e) {
241 			throw new ElmoMementoException(e);
242 		}
243 	}
244 
245 	public void redoMemento(Memento memento) {
246 		if (!(memento instanceof SesameMemento))
247 			throw new IllegalArgumentException();
248 		SesameMemento m = (SesameMemento) memento;
249 		try {
250 			m.redo(getConnection());
251 		} catch (RepositoryException e) {
252 			throw new ElmoMementoException(e);
253 		}
254 	}
255 
256 	public Object getInstance(Value value) {
257 		if (value instanceof Resource) {
258 			SesameEntity bean = this.find((Resource) value);
259 			if (logger.isDebugEnabled()) {
260 				try {
261 					if (!getConnection().hasStatement((Resource) value, null,
262 							null))
263 						logger.debug("Warning: Unknown entity: " + value);
264 				} catch (RepositoryException e) {
265 					throw new ElmoIOException(e);
266 				}
267 			}
268 			return bean;
269 		}
270 		return lm.getObject((Literal) value);
271 	}
272 
273 	public Value getValue(Object instance) {
274 		if (instance instanceof SesameEntity) {
275 			return ((SesameEntity) instance).getSesameResource();
276 		} else if (instance instanceof Entity) {
277 			return getValue(merge(instance));
278 		}
279 		Class<?> type = instance.getClass();
280 		if (lm.isTypeOfLiteral(type))
281 			return lm.getLiteral(instance);
282 		// is it a resource or a literal?
283 		Class<?>[] interfaces = type.getInterfaces();
284 		if (interfaces.length > 0) {
285 			for (Class<?> face : interfaces) {
286 				if (mapper.findType(face) != null)
287 					return getValue(merge(instance));
288 			}
289 		}
290 		// must be a literal
291 		return lm.getLiteral(instance);
292 	}
293 
294 	public Value getLocalizedValue(Object instance) {
295 		return lm.getLiteral(instance.toString(), language);
296 	}
297 
298 	public boolean contains(Object entity) {
299 		if (entity instanceof SesameEntity) {
300 			SesameEntity se = (SesameEntity) entity;
301 			return this.equals(se.getSesameManager());
302 		}
303 		return false;
304 	}
305 
306 	/**
307 	 * @deprecated Use {@link #designate(Class<T>)} instead
308 	 */
309 	public <T> T create(Class<T> concept) {
310 		return designate(concept);
311 	}
312 
313 	/**
314 	 * @deprecated Use {@link #designate(Class<T>,QName)} instead
315 	 */
316 	public <T> T create(Class<T> concept, QName qname) {
317 		return designate(concept, qname);
318 	}
319 
320 	/**
321 	 * @deprecated Use {@link #designate(Resource,Class<T>)} instead
322 	 */
323 	@SuppressWarnings("unchecked")
324 	public <T> T create(Resource resource, Class<T> concept) {
325 		return designate(resource, concept);
326 	}
327 
328 	public <T> T designate(Class<T> concept) {
329 		Resource resource = resources.createResource(null);
330 		return designate(resource, concept);
331 	}
332 
333 	public <T> T designate(Class<T> concept, QName qname) {
334 		Resource resource = resources.createResource(qname);
335 		return designate(resource, concept);
336 	}
337 
338 	@SuppressWarnings("unchecked")
339 	public <T> T designate(Resource resource, Class<T> concept) {
340 		Class[] roles = resources.mergeRole(resource, concept);
341 		Entity bean = createBean(resource, roles);
342 		assert !concept.isInterface()
343 				|| concept.isAssignableFrom(bean.getClass()) : "Concept has not bean recorded: "
344 				+ concept.getSimpleName();
345 		return (T) bean;
346 	}
347 
348 	public <T> T designateEntity(Class<T> concept, Object entity) {
349 		assert entity instanceof SesameEntity : entity;
350 		Resource resource = ((SesameEntity) entity).getSesameResource();
351 		return designate(resource, concept);
352 	}
353 
354 	public Entity removeDesignation(Class<?> concept, Object entity) {
355 		assert entity instanceof SesameEntity : entity;
356 		Resource resource = ((SesameEntity) entity).getSesameResource();
357 		Class[] roles = resources.removeRole(resource, concept);
358 		return createBean(resource, roles);
359 	}
360 
361 	public <T> T rename(T bean, QName qname) {
362 		Resource after = resources.createResource(qname);
363 		return rename(bean, after);
364 	}
365 
366 	@SuppressWarnings("unchecked")
367 	public <T> T rename(T bean, Resource destination) {
368 		assert bean instanceof SesameEntity;
369 		Resource before = ((SesameEntity) bean).getSesameResource();
370 		Class[] roles = resources.getRoles(before);
371 		resources.renameResource(before, destination);
372 		return (T) createBean(destination, roles);
373 	}
374 
375 	public SesameEntity find(QName qname) {
376 		return find(resources.createResource(qname));
377 	}
378 
379 	public QName getSesameBeanQName(SesameEntity bean) {
380 		Resource resource = bean.getSesameResource();
381 		return resources.createQName(resource);
382 	}
383 
384 	public SesameEntity find(Resource resource) {
385 		Class<?>[] roles = resources.getRoles(resource);
386 		return createBean(resource, roles);
387 	}
388 
389 	public <T> T find(Class<T> concept, Object qname) {
390 		assert qname instanceof QName : qname;
391 		SesameEntity entity = find((QName) qname);
392 		if (concept.isInstance(entity))
393 			return concept.cast(entity);
394 		return null;
395 	}
396 
397 	public <T> T getReference(Class<T> concept, Object qname) {
398 		return find(concept, qname);
399 	}
400 
401 	public void refresh(Object entity) {
402 		if (entity instanceof Refreshable) {
403 			((Refreshable) entity).refresh();
404 		}
405 	}
406 
407 	public <T> T merge(T bean) {
408 		Object result;
409 		if (bean instanceof Entity) {
410 			Entity eb = (Entity) bean;
411 			QName name = eb.getQName();
412 			result = designate(bean.getClass(), name);
413 		} else {
414 			result = designate(bean.getClass());
415 		}
416 		assert result instanceof Mergeable;
417 		((Mergeable) result).merge(bean);
418 		return (T) result;
419 	}
420 
421 	public void persist(Object bean) {
422 		merge(bean);
423 	}
424 
425 	public SesameQuery createQuery(String query) {
426 		try {
427 			TupleQuery qry = getConnection().prepareTupleQueryWithinContext(
428 					query);
429 			return new SesameQuery(this, qry);
430 		} catch (MalformedQueryException e) {
431 			throw new ElmoIOException(e);
432 		} catch (RepositoryException e) {
433 			throw new ElmoIOException(e);
434 		}
435 	}
436 
437 	public SesameQuery createNativeQuery(String serql) {
438 		try {
439 			TupleQuery qry = getConnection().prepareTupleQueryWithinContext(
440 					QueryLanguage.SERQL, serql, "");
441 			return new SesameQuery(this, qry);
442 		} catch (MalformedQueryException e) {
443 			throw new ElmoIOException(e);
444 		} catch (RepositoryException e) {
445 			throw new ElmoIOException(e);
446 		}
447 	}
448 
449 	public SesameQuery createNativeQuery(String serql, Class concept) {
450 		try {
451 			TupleQuery qry = getConnection().prepareTupleQueryWithinContext(
452 					QueryLanguage.SERQL, serql, "");
453 			return new SesameQuery(this, qry);
454 		} catch (MalformedQueryException e) {
455 			throw new ElmoIOException(e);
456 		} catch (RepositoryException e) {
457 			throw new ElmoIOException(e);
458 		}
459 	}
460 
461 	public SesameQuery createNativeQuery(String serql, String nil) {
462 		try {
463 			TupleQuery qry = getConnection().prepareTupleQueryWithinContext(
464 					QueryLanguage.SERQL, serql, "");
465 			return new SesameQuery(this, qry);
466 		} catch (MalformedQueryException e) {
467 			throw new ElmoIOException(e);
468 		} catch (RepositoryException e) {
469 			throw new ElmoIOException(e);
470 		}
471 	}
472 
473 	public Query createNamedQuery(String name) {
474 		throw new UnsupportedOperationException(
475 				"Named queries are not supported");
476 	}
477 
478 	public <T> Set<T> findAll(final Class<T> javaClass) {
479 		Set<T> set = new HashSet<T>();
480 		Iterator<Resource> iter = resources.createRoleQuery(javaClass);
481 		try {
482 			for (int count = 0; iter.hasNext(); count++) {
483 				set.add(javaClass.cast(find(iter.next())));
484 			}
485 			return Collections.unmodifiableSet(set);
486 		} finally {
487 			ElmoIteration.close(iter);
488 		}
489 	}
490 
491 	public void remove(Object entity) {
492 		assert entity instanceof SesameEntity : entity;
493 		Resource resource = ((SesameEntity) entity).getSesameResource();
494 		resources.removeResource(resource);
495 	}
496 
497 	Value createResource(QName qname) {
498 		return resources.createResource(qname);
499 	}
500 
501 	private SesameEntity createBean(Resource resource, Class<?>[] roles) {
502 		Class<?> type = resolver.resolveRoles(roles);
503 		try {
504 			Object obj = type.newInstance();
505 			assert obj instanceof SesameManagerAware : "core roles are not registered, check your deployed classpath";
506 			SesameManagerAware bean = (SesameManagerAware) obj;
507 			bean.initSesameManager(this);
508 			bean.initSesameResource(resource);
509 			return bean;
510 		} catch (InstantiationException e) {
511 			throw new ElmoCompositionException(e);
512 		} catch (IllegalAccessException e) {
513 			throw new ElmoCompositionException(e);
514 		}
515 	}
516 
517 	private FlushableConnection findFlushableConnection(
518 			RepositoryConnection conn) throws RepositoryException {
519 		if (conn instanceof FlushableConnection)
520 			return (FlushableConnection) conn;
521 		if (conn instanceof DelegatingRepositoryConnection) {
522 			DelegatingRepositoryConnection dconn = (DelegatingRepositoryConnection) conn;
523 			return findFlushableConnection((dconn).getDelegate());
524 		}
525 		return null;
526 	}
527 
528 	public void lock(Object entity, LockModeType mode) {
529 		throw new UnsupportedOperationException("locking is not supported");
530 	}
531 
532 }