/* * Copyright (c) 2004-2011 Marco Maccaferri 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: * Marco Maccaferri - initial API and implementation */ package org.eclipsetrader.repository.hibernate; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.ILock; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.MultiRule; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.IValueVariable; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.osgi.util.NLS; import org.eclipsetrader.core.feed.IFeedIdentifier; import org.eclipsetrader.core.repositories.IRepository; import org.eclipsetrader.core.repositories.IRepositoryRunnable; import org.eclipsetrader.core.repositories.IStore; import org.eclipsetrader.repository.hibernate.internal.Activator; import org.eclipsetrader.repository.hibernate.internal.RepositoryDefinition; import org.eclipsetrader.repository.hibernate.internal.RepositoryValidator; import org.eclipsetrader.repository.hibernate.internal.stores.HistoryStore; import org.eclipsetrader.repository.hibernate.internal.stores.IntradayHistoryStore; import org.eclipsetrader.repository.hibernate.internal.stores.RepositoryStore; import org.eclipsetrader.repository.hibernate.internal.stores.ScriptStore; import org.eclipsetrader.repository.hibernate.internal.stores.SecurityStore; import org.eclipsetrader.repository.hibernate.internal.stores.StrategyScriptProperties; import org.eclipsetrader.repository.hibernate.internal.stores.StrategyScriptStore; import org.eclipsetrader.repository.hibernate.internal.stores.TradeStore; import org.eclipsetrader.repository.hibernate.internal.stores.WatchListStore; import org.eclipsetrader.repository.hibernate.internal.types.DividendType; import org.eclipsetrader.repository.hibernate.internal.types.HistoryData; import org.eclipsetrader.repository.hibernate.internal.types.IdentifierPropertyType; import org.eclipsetrader.repository.hibernate.internal.types.IdentifierType; import org.eclipsetrader.repository.hibernate.internal.types.SecurityUnknownPropertyType; import org.eclipsetrader.repository.hibernate.internal.types.SplitData; import org.eclipsetrader.repository.hibernate.internal.types.WatchListColumn; import org.eclipsetrader.repository.hibernate.internal.types.WatchListHolding; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.event.PostDeleteEvent; import org.hibernate.event.PostDeleteEventListener; import org.hibernate.event.PostInsertEvent; import org.hibernate.event.PostInsertEventListener; import org.hibernate.tool.hbm2ddl.SchemaExport; import org.hibernate.tool.hbm2ddl.SchemaUpdate; public class HibernateRepository implements IRepository, ISchedulingRule, IExecutableExtension { public static final String URI_SECURITY_PART = "securities"; public static final String URI_SECURITY_HISTORY_PART = "securities/history"; public static final String URI_SECURITY_INTRADAY_HISTORY_PART = "securities/history/{0}/{1}"; public static final String URI_WATCHLIST_PART = "watchlists"; public static final String URI_TRADE_PART = "trades"; public static final String URI_SCRIPT_PART = "scripts"; public static final String URI_STRATEGY_PART = "strategies"; private static final String ERROR_MESSAGE = "Errors occurred updating database"; private String schema; private String name; private Properties properties; private RepositoryDefinition repositoryDefinition; private Session session; private Map<String, IdentifierType> identifiersMap; private List<WatchListStore> watchlists; private Map<URI, IStore> uriMap = new HashMap<URI, IStore>(); private IJobManager jobManager; private final ILock lock; public HibernateRepository() { this(null, null, new Properties()); } public HibernateRepository(RepositoryDefinition repositoryDefinition) { this(repositoryDefinition.getSchema(), repositoryDefinition.getLabel(), repositoryDefinition.getProperties()); this.repositoryDefinition = repositoryDefinition; } public HibernateRepository(String schema, String name, Properties properties) { this.schema = schema; this.name = name; this.properties = properties; this.identifiersMap = new HashMap<String, IdentifierType>(); jobManager = Job.getJobManager(); lock = jobManager.newLock(); } /* (non-Javadoc) * @see org.eclipse.core.runtime.IExecutableExtension#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, java.lang.String, java.lang.Object) */ @Override public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException { this.schema = config.getAttribute("scheme"); this.name = config.getAttribute("name"); IStringVariableManager variableManager = VariablesPlugin.getDefault().getStringVariableManager(); String[] propertyNames = new String[] { "file.encoding", "file.separator", "java.home", "user.dir", "user.home", "user.language", "user.name", "user.timezone", }; List<IValueVariable> variables = new ArrayList<IValueVariable>(); if (variableManager.getValueVariable("workspace_loc") == null) { variables.add(variableManager.newValueVariable("workspace_loc", "Returns the absolute file system path of the workspace root.", true, Platform.getLocation().toOSString())); } for (String k : propertyNames) { if (variableManager.getValueVariable(k) == null) { variables.add(variableManager.newValueVariable(k, "", true, System.getProperty(k))); } } variableManager.addVariables(variables.toArray(new IValueVariable[variables.size()])); for (IConfigurationElement element : config.getChildren()) { String key = element.getAttribute("name"); String value = element.getAttribute("value"); this.properties.put(key, variableManager.performStringSubstitution(value)); } try { startUp(null); Activator.getDefault().getRepositories().add(this); } catch (Exception e) { String message = NLS.bind("Error loading repository '{1}' ({0})", new Object[] { schema, name }); Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, message, e); Activator.log(status); } } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#getSchema() */ @Override public String getSchema() { return schema; } @SuppressWarnings("serial") protected AnnotationConfiguration buildConfiguration() { AnnotationConfiguration cfg = new AnnotationConfiguration(); cfg.setProperties(properties); cfg.addAnnotatedClass(IdentifierPropertyType.class); cfg.addAnnotatedClass(IdentifierType.class); cfg.addAnnotatedClass(WatchListColumn.class); cfg.addAnnotatedClass(WatchListHolding.class); cfg.addAnnotatedClass(HistoryData.class); cfg.addAnnotatedClass(DividendType.class); cfg.addAnnotatedClass(SplitData.class); cfg.addAnnotatedClass(SecurityUnknownPropertyType.class); cfg.addAnnotatedClass(SecurityStore.class); cfg.addAnnotatedClass(WatchListStore.class); cfg.addAnnotatedClass(HistoryStore.class); cfg.addAnnotatedClass(IntradayHistoryStore.class); cfg.addAnnotatedClass(TradeStore.class); cfg.addAnnotatedClass(ScriptStore.class); cfg.addAnnotatedClass(StrategyScriptStore.class); cfg.addAnnotatedClass(StrategyScriptProperties.class); cfg.getEventListeners().setPostInsertEventListeners(new PostInsertEventListener[] { new PostInsertEventListener() { @Override public void onPostInsert(PostInsertEvent event) { if (event.getEntity() instanceof IdentifierType) { identifiersMap.put(((IdentifierType) event.getEntity()).getSymbol(), (IdentifierType) event.getEntity()); } if (event.getEntity() instanceof SecurityStore) { uriMap.put(((SecurityStore) event.getEntity()).toURI(), (SecurityStore) event.getEntity()); } if (event.getEntity() instanceof WatchListStore) { uriMap.put(((WatchListStore) event.getEntity()).toURI(), (WatchListStore) event.getEntity()); } if (event.getEntity() instanceof ScriptStore) { uriMap.put(((ScriptStore) event.getEntity()).toURI(), (ScriptStore) event.getEntity()); } if (event.getEntity() instanceof StrategyScriptStore) { uriMap.put(((StrategyScriptStore) event.getEntity()).toURI(), (StrategyScriptStore) event.getEntity()); } } }, }); cfg.getEventListeners().setPostDeleteEventListeners(new PostDeleteEventListener[] { new PostDeleteEventListener() { @Override public void onPostDelete(PostDeleteEvent event) { if (event.getEntity() instanceof IdentifierType) { identifiersMap.remove(((IdentifierType) event.getEntity()).getSymbol()); } if (event.getEntity() instanceof SecurityStore) { uriMap.remove(((SecurityStore) event.getEntity()).toURI()); } if (event.getEntity() instanceof WatchListStore) { uriMap.remove(((WatchListStore) event.getEntity()).toURI()); } if (event.getEntity() instanceof ScriptStore) { uriMap.remove(((ScriptStore) event.getEntity()).toURI()); } if (event.getEntity() instanceof StrategyScriptStore) { uriMap.remove(((StrategyScriptStore) event.getEntity()).toURI()); } } }, }); return cfg; } public void startUp(IProgressMonitor monitor) { properties.put("hibernate.query.factory_class", "org.hibernate.hql.classic.ClassicQueryTranslatorFactory"); properties.put("hibernate.connection.pool_size", "5"); properties.put("hibernate.jdbc.batch_size", "20"); properties.put("hibernate.show_sql", "false"); // Build suitable defaults for file-based databases (Apache Derby and HSQL) if (!properties.containsKey("hibernate.connection.url") && Activator.getDefault() != null) { if ("org.apache.derby.jdbc.EmbeddedDriver".equals(properties.get("hibernate.connection.driver_class"))) { properties.put("hibernate.connection.url", "jdbc:derby:" + Activator.getDefault().getStateLocation().toOSString() + "/.derby;create=true"); } if ("org.hsqldb.jdbcDriver".equals(properties.get("hibernate.connection.driver_class"))) { properties.put("hibernate.connection.url", "jdbc:hsqldb:file:" + Activator.getDefault().getStateLocation().toOSString() + "/.hsqldb"); } } AnnotationConfiguration cfg = buildConfiguration(); try { initializeDatabase(cfg); } catch (Exception e) { String message = NLS.bind("Error initializing repository '{1}' ({0})", new Object[] { schema, name }); Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, message, e); Activator.log(status); int userChoice = new RepositoryValidator(name, cfg).validate(); switch (userChoice) { case RepositoryValidator.UPDATE_ID: SchemaUpdate schemaUpdate = new SchemaUpdate(cfg); schemaUpdate.execute(true, true); if (schemaUpdate.getExceptions().size() != 0) { MultiStatus multiStatus = new MultiStatus(Activator.PLUGIN_ID, 0, new IStatus[0], ERROR_MESSAGE, null); for (Object o : schemaUpdate.getExceptions()) { multiStatus.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, null, (Exception) o)); } Activator.log(multiStatus); } break; case RepositoryValidator.CREATE_ID: SchemaExport schemaExport = new SchemaExport(cfg); schemaExport.create(true, true); if (schemaExport.getExceptions().size() != 0) { MultiStatus multiStatus = new MultiStatus(Activator.PLUGIN_ID, 0, new IStatus[0], ERROR_MESSAGE, null); for (Object o : schemaExport.getExceptions()) { multiStatus.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, null, (Exception) o)); } Activator.log(multiStatus); } break; } } initializeDatabase(cfg); } @SuppressWarnings("unchecked") void initializeDatabase(AnnotationConfiguration cfg) { SessionFactory sessionFactory = cfg.buildSessionFactory(); session = sessionFactory.openSession(); List<IdentifierType> identifiers = session.createCriteria(IdentifierType.class).list(); for (IdentifierType identifierType : identifiers) { identifiersMap.put(identifierType.getSymbol(), identifierType); } List<SecurityStore> securities = session.createCriteria(SecurityStore.class).list(); for (SecurityStore store : securities) { store.setRepository(this); uriMap.put(store.toURI(), store); } List<ScriptStore> scripts = session.createCriteria(ScriptStore.class).list(); for (ScriptStore store : scripts) { store.setRepository(this); uriMap.put(store.toURI(), store); } List<StrategyScriptStore> strategies = session.createCriteria(StrategyScriptStore.class).list(); for (StrategyScriptStore store : strategies) { store.setRepository(this); uriMap.put(store.toURI(), store); } } public void shutDown(IProgressMonitor monitor) { try { if (session != null) { session.close(); } } catch (Exception e) { String message = NLS.bind("Error shutting down repository {0}:{1}", new Object[] { schema, name }); Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, message, e); Activator.log(status); } } @SuppressWarnings("unchecked") protected synchronized void initializeWatchListsCollections() { if (watchlists == null) { try { watchlists = session.createCriteria(WatchListStore.class).list(); for (WatchListStore store : watchlists) { store.setRepository(this); uriMap.put(store.toURI(), store); } } catch (Exception e) { String message = NLS.bind("Error loading repository {0}:{1}", new Object[] { schema, name }); Status status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, message, e); Activator.log(status); } } if (watchlists == null) { watchlists = new ArrayList<WatchListStore>(); } } public Session getSession() { return session; } public IdentifierType getIdentifierTypeFromFeedIdentifier(IFeedIdentifier feedIdentifier) { IdentifierType type = identifiersMap.get(feedIdentifier.getSymbol()); if (type == null) { type = new IdentifierType(feedIdentifier); identifiersMap.put(type.getSymbol(), type); } return type; } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#canDelete() */ @Override public boolean canDelete() { return true; } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#canWrite() */ @Override public boolean canWrite() { return true; } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#createObject() */ @Override public IStore createObject() { return new RepositoryStore(this); } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#fetchObjects(org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStore[] fetchObjects(IProgressMonitor monitor) { if (watchlists == null) { initializeWatchListsCollections(); } List<IStore> list = new ArrayList<IStore>(); list.addAll(uriMap.values()); return list.toArray(new IStore[list.size()]); } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#getObject(java.net.URI) */ @Override public IStore getObject(URI uri) { if (URI_WATCHLIST_PART.equals(uri.getSchemeSpecificPart())) { if (watchlists == null) { initializeWatchListsCollections(); } } return uriMap.get(uri); } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#runInRepository(org.eclipsetrader.core.repositories.IRepositoryRunnable, org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus runInRepository(IRepositoryRunnable runnable, IProgressMonitor monitor) { return runInRepository(runnable, this, monitor); } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IRepository#runInRepository(org.eclipsetrader.core.repositories.IRepositoryRunnable, org.eclipse.core.runtime.jobs.ISchedulingRule, org.eclipse.core.runtime.IProgressMonitor) */ @Override public IStatus runInRepository(IRepositoryRunnable runnable, ISchedulingRule rule, IProgressMonitor monitor) { IStatus status; jobManager.beginRule(rule, monitor); Transaction currentTransaction = null; try { lock.acquire(); currentTransaction = session.beginTransaction(); try { status = runnable.run(monitor); session.flush(); currentTransaction.commit(); currentTransaction = null; } catch (Exception e) { status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error running repository task", e); //$NON-NLS-1$ Activator.log(status); } catch (LinkageError e) { status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error running repository task", e); //$NON-NLS-1$ Activator.log(status); } } catch (Exception e) { status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error running repository task", e); //$NON-NLS-1$ Activator.log(status); try { currentTransaction.rollback(); } catch (Exception e1) { Status status1 = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 0, "Error rolling back transaction", e1); //$NON-NLS-1$ Activator.log(status1); } } finally { lock.release(); jobManager.endRule(rule); } session.clear(); return status; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule) */ @Override public boolean contains(ISchedulingRule rule) { if (this == rule) { return true; } if (rule instanceof MultiRule) { MultiRule multi = (MultiRule) rule; ISchedulingRule[] children = multi.getChildren(); for (int i = 0; i < children.length; i++) { if (!contains(children[i])) { return false; } } return true; } return false; } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule) */ @Override public boolean isConflicting(ISchedulingRule rule) { if (this == rule) { return true; } return false; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public Object getAdapter(Class adapter) { if (repositoryDefinition != null && adapter.isAssignableFrom(repositoryDefinition.getClass())) { return repositoryDefinition; } if (adapter.isAssignableFrom(getClass())) { return this; } return null; } public RepositoryDefinition getRepositoryDefinition() { return repositoryDefinition; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return name; } }