Java tutorial
/** * <copyright> * * Copyright (c) 2005, 2006, 2007, 2008, 2011 Springsite BV (The Netherlands) and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Martin Taal * </copyright> * * $Id: HibernateResource.java,v 1.29 2011/02/21 05:23:20 mtaal Exp $ */ package org.eclipse.emf.teneo.hibernate.resource; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.teneo.hibernate.EMFInterceptor; import org.eclipse.emf.teneo.hibernate.HbDataStore; import org.eclipse.emf.teneo.hibernate.HbHelper; import org.eclipse.emf.teneo.hibernate.HbMapperException; import org.eclipse.emf.teneo.hibernate.HbSessionWrapper; import org.eclipse.emf.teneo.hibernate.HbUtil; import org.eclipse.emf.teneo.hibernate.SessionWrapper; import org.eclipse.emf.teneo.hibernate.mapping.identifier.IdentifierCacheHandler; import org.eclipse.emf.teneo.hibernate.mapping.identifier.IdentifierUtil; import org.eclipse.emf.teneo.resource.StoreResource; import org.eclipse.emf.teneo.util.AssertUtil; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.criterion.Restrictions; import org.hibernate.engine.SessionImplementor; import org.hibernate.impl.SessionImpl; import org.hibernate.metadata.ClassMetadata; /** * Hibernate Resource. The hibernate resource has a Session during its lifetime. * A transaction is started before the load and it is stopped just after the * save. The session is disconnected and reconnected when loading and saving. * * When you create a HbDataStore through the appropriate method in the * HibernateHelper class. The name you passed there can be used as a parameter * in the uri used to create this resource (using the parameter pmfname). The * uri is then: hibernate://?dsname=myemf. * * Another simple trick which is used to fool emf a bit is that the extension of * the uri can also be used to init a hibernate resource! * * @author <a href="mailto:mtaal@elver.org">Martin Taal</a> * @version $Revision: 1.29 $ */ public class HibernateResource extends StoreResource implements HbResource { /** The logger */ private static Log log = LogFactory.getLog(HibernateResource.class); /** The fragment separator */ private static String SEPARATOR = "|"; /** The store used to determine where to query for the data */ protected HbDataStore emfDataStore; /** The uri parameter under which to store a session controller */ public static String SESSION_CONTROLLER_PARAM = "sessionController"; /** The session wrapper used for long transactions */ protected SessionWrapper sessionWrapper = null; // is opened at first load /** The session controller */ protected SessionController sessionController = null; /** Is set to true if there is a sessionController */ private boolean hasSessionController = false; /** * The constructor, gets an uri and retrieves the backing OJBStore */ public HibernateResource(URI uri) { super(uri); if (log.isDebugEnabled()) { log.debug("Creating hibernateresource using uri: " + uri.toString()); } final Map<String, String> params = decodeQueryString(uri.query()); if (params.get(DS_NAME_PARAM) != null) { // only the name setDefinedQueries(getQueries(params)); emfDataStore = HbHelper.INSTANCE.getDataStore(getParam(params, DS_NAME_PARAM, uri.query())); } else if (params.get(SESSION_CONTROLLER_PARAM) != null) { setDefinedQueries(getQueries(params)); final String scName = getParam(params, SESSION_CONTROLLER_PARAM, uri.query()); sessionController = SessionController.getSessionController(scName); if (log.isDebugEnabled()) { log.debug("Using session controller " + scName); } emfDataStore = sessionController.getHbDataStore(); hasSessionController = true; } else if (uri.fileExtension() != null) // this is probably a platform // uri! { if (log.isDebugEnabled()) { log.debug("Trying fileextension: " + uri.fileExtension()); } // then try the extension of the resource emfDataStore = HbHelper.INSTANCE.getDataStore(uri.fileExtension()); // if null then assume that this is a properties file if (emfDataStore == null) { if (log.isDebugEnabled()) { log.debug("No datastore defined for extension, assuming this is a property file " + uri.toString()); } try { final URIConverter uriConverter = getURIConverter(); final InputStream is = uriConverter.createInputStream(uri); final Properties props = new Properties(); props.load(is); is.close(); emfDataStore = HbUtil.getCreateDataStore(props); setDefinedQueries(getQueries(props)); } catch (IOException e) { throw new HbMapperException("Exception when reading properties from: " + uri.toString(), e); } } } if (emfDataStore == null) { throw new HbMapperException("No HbDataStore can be found using the uri " + uri.toString()); } if (log.isDebugEnabled()) { log.debug("Using emf data store using " + emfDataStore.getName()); } super.init(emfDataStore.getTopEntities()); } /** Returns the emfdatastore */ public HbDataStore getEMFDataStore() { return emfDataStore; } /** * Returns the session of this resource, if no session is set yet then * creates it using the datastore. As a default the FlushMode is set to * Never. * * Deprecated, use getSessionWrapper (to support ejb3) */ @Deprecated public Session getSession() { return (Session) getSessionWrapper().getSession(); } /** Return the sessionwrapper */ public SessionWrapper getSessionWrapper() { if (sessionWrapper == null) { if (hasSessionController) { sessionWrapper = sessionController.getSessionWrapper(); } else { // session can be null when this is an xml import! ; sessionWrapper = emfDataStore.createSessionWrapper(); sessionWrapper.setFlushModeManual(); } } return sessionWrapper; } /** * Sets the session, overwrites current session. Deprecated use * setSessionWrapper. */ @Deprecated public void setSession(Session session) { if (session != null) { this.sessionWrapper = new HbSessionWrapper(emfDataStore, session); } else { this.sessionWrapper = null; } } /** * Unpacks the id string and reads an object from the db, note for each read * a transaction is opened, unless the session is controlled by the caller. * * @see org.eclipse.emf.ecore.resource.impl.ResourceImpl#getEObjectByID(java.lang.String) */ @Override protected EObject getEObjectByID(String id) { // try to find the different parts of the id if (id == null) { return super.getEObjectByID(id); } if (getIntrinsicIDToEObjectMap() != null) { final EObject firstCheck = getIntrinsicIDToEObjectMap().get(id); if (firstCheck != null) { return firstCheck; } } if (log.isDebugEnabled()) { log.debug("Reading eobject using urifragment " + id); } final String[] parts = id.split("\\" + SEPARATOR); if (parts.length != 2) { if (log.isDebugEnabled()) { log.debug("Not a valid urifragment (" + id + ") for the hibernate resource, trying the superclass"); } return super.getEObjectByID(id); } // build a query final EClass eclass = emfDataStore.getEntityNameStrategy().toEClass(parts[0]); final int splitIndex = parts[1].indexOf("="); if (splitIndex == -1) { if (log.isDebugEnabled()) { log.debug("Not a valid urifragment (" + id + ") for the hibernate resource, trying the superclass"); } return super.getEObjectByID(id); } final String idStr = parts[1].substring(1 + splitIndex); // try to find the object using the id-part final EObject eObject = super.getEObjectByID(idStr); if (eObject != null) { return eObject; } final boolean oldLoading = isLoading(); boolean err = true; try { setIsLoading(true); if (!hasSessionController) { getSessionWrapper().beginTransaction(); } final Object result = getSessionWrapper().get(parts[0], (Serializable) HbUtil.stringToId(eclass, emfDataStore, idStr)); if (result == null) { if (log.isDebugEnabled()) { log.debug("Object not found in the db, trying the parent"); } err = false; return super.getEObjectByID(id); } final InternalEObject internalEObject = (InternalEObject) result; // only add if not yet part of a resource if (internalEObject.eResource() == null) { addUsingContainmentStructure((InternalEObject) result); } err = false; return (EObject) result; } finally { setIsLoading(oldLoading); if (!hasSessionController) { if (err) { getSessionWrapper().rollbackTransaction(); getSessionWrapper().close(); } else { getSessionWrapper().commitTransaction(); } } } } /** * Creates a unique id string from the eobject. The id string will contain a * link to the type (eclass) and the string version of the id itself. This * method assumes that the id can be converted from and to a string! * * @see org.eclipse.emf.ecore.resource.impl.ResourceImpl#getURIFragment(org.eclipse.emf.ecore.EObject) */ @Override public String getURIFragment(EObject object) { if (object == null) { return null; } final String theId = HbUtil.idToString(object, emfDataStore); if (theId == null) { return super.getURIFragment(object); } final StringBuffer idStr = new StringBuffer(); idStr.append(emfDataStore.getEntityNameStrategy().toEntityName(object.eClass())); idStr.append(SEPARATOR); idStr.append("id=" + theId); return idStr.toString(); } /** * Sets the sessionwrapper, overwrites current session. */ public void setSessionWrapper(SessionWrapper sessionWrapper) { this.sessionWrapper = sessionWrapper; } /** Returns the session, does nothing in this impl */ public void returnSession(Session theSession) { // do nothing } /** * Returns the sessionwrapper to the resource so that it can do clean up (or * not) */ public void returnSessionWrapper(SessionWrapper sessionWrapper) { } /** * Returns an array of EObjects which refer to a certain EObject, note if * the array is of length zero then no refering EObjects where found. */ @Override public Object[] getCrossReferencers(EObject referedTo) { boolean err = true; final SessionWrapper mySessionWrapper = getSessionWrapper(); try { if (!hasSessionController) { mySessionWrapper.beginTransaction(); } final Object[] result = emfDataStore.getCrossReferencers(mySessionWrapper, referedTo); err = false; return result; } catch (Exception e) { e.printStackTrace(System.err); throw new HbMapperException("Exception when doing cross reference search " + emfDataStore.getName(), e); } finally { if (!hasSessionController) { if (err) { mySessionWrapper.rollbackTransaction(); mySessionWrapper.close(); } else { mySessionWrapper.commitTransaction(); } } } } /** * Saves the changed objects or removes the detached objects from this * resource. */ @Override protected void saveResource(Map<?, ?> options) { if (log.isDebugEnabled()) { log.debug("Saving resource with uri: " + getURI()); } boolean err = true; final SessionWrapper mySessionWrapper = getSessionWrapper(); try { if (!hasSessionController) { mySessionWrapper.beginTransaction(); } for (EObject eobject : super.getContents()) { mySessionWrapper.saveOrUpdate(eobject); } // delete all deleted objects for (Object obj : removedEObjects) { final InternalEObject eobj = (InternalEObject) obj; if (eobj.eResource() != null && eobj.eResource() != this) { continue; } if (IdentifierUtil.getID(obj, (SessionImplementor) mySessionWrapper.getHibernateSession()) != null) // persisted // object { if (eobj.eDirectResource() == null || eobj.eDirectResource() == this) { mySessionWrapper.delete(obj); EMFInterceptor.registerCollectionsForDereferencing((EObject) obj); } } } // now flush everything if (!hasSessionController) { mySessionWrapper.flush(); } err = false; } catch (Exception e) { e.printStackTrace(System.err); throw new HbMapperException("Exception when saving resource " + emfDataStore.getName(), e); } finally { if (!hasSessionController) { if (err) { beforeSaveRollback(); mySessionWrapper.rollbackTransaction(); afterSaveRollback(); // see bugzilla 221950 // mySessionWrapper.close(); } else { mySessionWrapper.commitTransaction(); } } } } /** * Override point, the default implementation will check if Hibernate * already assigned an id to an eobject. If so it is restored back to null. * See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=331953 */ protected void beforeSaveRollback() { // assigned ids Session session = sessionWrapper.getHibernateSession(); for (EObject eobject : super.getNewEObjects()) { String entityName = session.getEntityName(eobject); String identifierName = getIdentifierName(eobject, session); Serializable id = session.getIdentifier(eobject); Criteria exists = session.createCriteria(entityName).add(Restrictions.eq(identifierName, id)); if (exists.uniqueResult() == null) { rollbackID(eobject, identifierName); } } } /** * Override point, default implementation is empty. */ protected void afterSaveRollback() { } /** * Resets the id back to null, identifierName can be null (in that case no * action is done, only the cached id in the identifier cache handler is * reset back). * * @see IdentifierCacheHandler#setID(Object, Object) */ protected void rollbackID(EObject eobject, String identifierName) { IdentifierCacheHandler.getInstance().setID(eobject, null); if (identifierName == null) { return; } EStructuralFeature identifier = eobject.eClass().getEStructuralFeature(identifierName); if (identifier == null) { return; } eobject.eUnset(identifier); } protected String getIdentifierName(EObject eobject, Session hs) { String entityName = hs.getEntityName(eobject); if (entityName == null) { return null; } ClassMetadata entityMetaData = hs.getSessionFactory().getClassMetadata(entityName); if (entityMetaData == null) { return null; } String identifierName = entityMetaData.getIdentifierPropertyName(); return identifierName; } /** * Loads all the objects in the global list */ @Override protected List<EObject> loadResource(Map<?, ?> options) { if (log.isDebugEnabled()) { log.debug("Loading resource: " + getURI().toString()); } // first clear the old list boolean err = true; final SessionWrapper mySessionWrapper = getSessionWrapper(); try { if (!hasSessionController) { mySessionWrapper.beginTransaction(); } // note we have to a call to the super class otherwise an infinite // loop is created final List<EObject> storeList = loadFromStore(mySessionWrapper); if (log.isDebugEnabled()) { log.debug("Loaded " + storeList.size() + " objects"); } err = false; return storeList; } catch (Exception e) { e.printStackTrace(System.err); throw new HbMapperException("Exception when saving resource " + emfDataStore.getName(), e); } finally { if (!hasSessionController) { if (err) { mySessionWrapper.rollbackTransaction(); mySessionWrapper.close(); } else { mySessionWrapper.commitTransaction(); } } } } /** * Rollsback the transaction if any and clears different lists to start with * an empty resource again. */ @Override protected void doUnload() { super.doUnload(); if (!hasSessionController) { if (!getSessionWrapper().isEJB3EntityManager()) { AssertUtil.assertTrue("Session must be disconnected in unload", !((SessionImpl) getSessionWrapper().getSession()).isTransactionInProgress()); } if (log.isDebugEnabled()) { log.debug("Doing unload, closing and nullifying session"); } getSessionWrapper().close(); setSessionWrapper(null); } else if (log.isDebugEnabled()) { log.debug( "Doing unload, has session controller, sessioncontroller is therefor responsible for session close"); } } /** * This method can be overridden to implement specific load behavior. Note * that a transaction has already been started. The session is passed as a * parameter, this is the same session which can be retrieved using the * getSession method. The read objects should be returned in the list. Note * that after this call the retrieved objects are put in the resource * content. */ protected List<EObject> loadFromStore(SessionWrapper sess) { if (definedQueriesPresent()) { return loadUsingDefinedQueries(sess); } else { return loadUsingTopClasses(sess); } } /** Reads data based on the topclasses list */ private ArrayList<EObject> loadUsingTopClasses(SessionWrapper sess) { if (log.isDebugEnabled()) { log.debug("Loading resource " + getURI() + " using top classes"); } final ArrayList<EObject> readObjects = new ArrayList<EObject>(); for (final String topClassName : topClassNames) { if (log.isDebugEnabled()) { log.debug("Loading objects using hql: FROM " + topClassName); } final List<?> qryResult; if (sess.isEJB3EntityManager()) { qryResult = sess.executeQuery("select o from " + topClassName + " o"); } else { qryResult = sess.executeQuery("from " + topClassName); } final Iterator<?> it = qryResult.iterator(); while (it.hasNext()) { final EObject eobj = (EObject) it.next(); // extra check on container because sometimes contained items // are still read in // case of multiple inheritance if (eobj.eContainer() == null) { readObjects.add(eobj); } } } return readObjects; } /** Reads data based using defined queries */ private ArrayList<EObject> loadUsingDefinedQueries(SessionWrapper sess) { if (log.isDebugEnabled()) { log.debug("Loading resource " + getURI() + " using defined queries"); } final ArrayList<EObject> readObjects = new ArrayList<EObject>(); final String[] qrys = getDefinedQueries(); for (String element : qrys) { final List<?> qryResult = sess.executeQuery(element); if (log.isDebugEnabled()) { log.debug("Loading objects using hql: " + element); } final Iterator<?> it = qryResult.iterator(); while (it.hasNext()) { final Object obj = it.next(); readObjects.add((EObject) obj); } } return readObjects; } /** Reads a set of objects into the resource by using a query. */ public Object[] getObjectsByQuery(String query, boolean cache) { if (log.isDebugEnabled()) { log.debug("Started listing objects by query " + query + " in resource " + getURI()); } SessionWrapper mySessionWrapper = null; boolean err = true; setIsLoading(true); try { mySessionWrapper = getSessionWrapper(); if (!hasSessionController) { mySessionWrapper.beginTransaction(); } final List<?> qryResult = mySessionWrapper.executeQuery(query, cache); for (Object object : qryResult) { if (object instanceof InternalEObject) { final InternalEObject eObject = (InternalEObject) object; // only add if the object is not already part of this // resource. // if already part of this resource then it should have been // loaded through // a containment relation. assert (eObject.eResource() != this || loadedEObjectSet.contains(eObject) || newEObjects.contains(eObject)); addToContent(eObject); } } err = false; if (log.isDebugEnabled()) { log.debug("Listed " + qryResult.size() + " objects using query " + query + " in resource " + getURI()); } return qryResult.toArray(); } finally { if (!hasSessionController) { if (err) { mySessionWrapper.rollbackTransaction(); mySessionWrapper.close(); } else { mySessionWrapper.commitTransaction(); } } setIsLoading(false); if (log.isDebugEnabled()) { log.debug("Finished getting objects by query " + query + " in resource " + getURI()); } } } /** * @return the hasSessionController */ public boolean isHasSessionController() { return hasSessionController; } @Override public boolean isLoading() { return isLoading; } /** Load additional objects into the contents using a query */ public Object[] listByQuery(String query, boolean cache) { if (log.isDebugEnabled()) { log.debug("Started listing objects by query " + query + " in resource " + getURI()); } SessionWrapper mySessionWrapper = null; boolean err = true; setIsLoading(true); final Notification notification = setLoaded(true); try { mySessionWrapper = getSessionWrapper(); if (!hasSessionController) { mySessionWrapper.beginTransaction(); } final List<?> qryResult = mySessionWrapper.executeQuery(query, cache); for (Object object : qryResult) { if (object instanceof InternalEObject) { addToContent((InternalEObject) object); } } err = false; if (log.isDebugEnabled()) { log.debug("Listed " + qryResult.size() + " objects using query " + query + " in resource " + getURI()); } return qryResult.toArray(); } finally { setIsLoading(false); if (!hasSessionController) { if (err) { mySessionWrapper.rollbackTransaction(); mySessionWrapper.close(); } else { mySessionWrapper.commitTransaction(); } } if (notification != null) { eNotify(notification); } if (log.isDebugEnabled()) { log.debug("Finished listing objects by query " + query + " in resource " + getURI()); } } } }