/* license-start
*
* Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, at <http://www.gnu.org/licenses/>.
*
* Contributors:
* Crispico - Initial API and implementation
*
* license-end
*/
package org.flowerplatform.web.database;
import java.io.IOException;
import java.security.Policy;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import javax.security.auth.Subject;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.teneo.PersistenceOptions;
import org.eclipse.emf.teneo.hibernate.HbDataStore;
import org.eclipse.emf.teneo.hibernate.HbHelper;
import org.eclipse.emf.teneo.hibernate.resource.HibernateResource;
import org.flowerplatform.common.CommonPlugin;
import org.flowerplatform.common.FlowerProperties;
import org.flowerplatform.common.FlowerProperties.AddProperty;
import org.flowerplatform.common.FlowerProperties.AddBooleanProperty;
import org.flowerplatform.emf_model.notation.Bounds;
import org.flowerplatform.emf_model.notation.Diagram;
import org.flowerplatform.emf_model.notation.Node;
import org.flowerplatform.emf_model.notation.NotationFactory;
import org.flowerplatform.emf_model.notation.NotationPackage;
import org.flowerplatform.web.entity.DBVersion;
import org.flowerplatform.web.entity.EntityFactory;
import org.flowerplatform.web.entity.EntityPackage;
import org.flowerplatform.web.entity.Group;
import org.flowerplatform.web.entity.Organization;
import org.flowerplatform.web.entity.OrganizationMembershipStatus;
import org.flowerplatform.web.entity.OrganizationUser;
import org.flowerplatform.web.entity.User;
import org.flowerplatform.web.entity.WorkingDirectory;
import org.flowerplatform.web.security.dto.OrganizationAdminUIDto;
import org.flowerplatform.web.security.permission.AdminSecurityEntitiesPermission;
import org.flowerplatform.web.security.permission.FlowerWebFilePermission;
import org.flowerplatform.web.security.permission.ModifyTreePermissionsPermission;
import org.flowerplatform.web.security.sandbox.FlowerWebPolicy;
import org.flowerplatform.web.security.sandbox.FlowerWebPrincipal;
import org.flowerplatform.web.security.sandbox.SecurityEntityListener;
import org.flowerplatform.web.security.service.OrganizationService;
import org.flowerplatform.web.security.service.UserService;
import org.flowerplatform.web.temp.GeneralService;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.internal.SessionFactoryImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DatabaseManager {
private static final Logger logger = LoggerFactory.getLogger(DatabaseManager.class);
private static final String PROP_DB_PERSISTENCE_UNIT = "database.persistence-unit";
private static final String PROP_DEFAULT_PROD_PERSISTENCE_UNIT = "production";
private static final String PROP_DB_INIT_WITH_TEST_DATA = "database.init-with-test-data";
private static final String PROP_DB_INIT_WITH_TEST_DATA_INTERNAL = "internal";
public static final String DS_NAME = "flowerDataStore";
private SessionFactory factory;
public DatabaseManager() {
Policy.setPolicy(new FlowerWebPolicy(Policy.getPolicy())); // FlowerWebPolicy uses the permissionServer.
// adding properties
CommonPlugin.getInstance().getFlowerProperties().addProperty(new AddProperty(PROP_DB_PERSISTENCE_UNIT, PROP_DEFAULT_PROD_PERSISTENCE_UNIT) {
@Override
protected String validateProperty(String input) {
// nothing to do; if the persistence unit is not found, an exception will be
// thrown in the code below
return null;
}
});
CommonPlugin.getInstance().getFlowerProperties().addProperty(new AddBooleanProperty(PROP_DB_INIT_WITH_TEST_DATA, "false") {
@Override
protected String validateProperty(String input) {
if (PROP_DB_INIT_WITH_TEST_DATA_INTERNAL.equals(input)) {
return null;
} else {
return super.validateProperty(input);
}
}
});
}
/**
* @author Mariana
*/
public void initialize() {
// Configuration config = new Configuration();
// Properties props = config.getProperties();
Properties props = new Properties();
// props.setProperty(Environment.DRIVER, "org.postgresql.Driver");
// props.setProperty(Environment.USER, "postgres");
// props.setProperty(Environment.URL, "jdbc:postgresql://localhost/flower-dev-center");
// props.setProperty(Environment.PASS, "postgres");
// props.setProperty(Environment.DIALECT, PostgresPlusDialect.class.getName());
// props.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread");
// props.setProperty(Environment.HBM2DDL_AUTO, "create-drop");
// props.setProperty(Environment.SHOW_SQL, "true");
props.setProperty(Environment.DRIVER, "org.h2.Driver");
props.setProperty(Environment.USER, "sa");
props.setProperty(Environment.URL, "jdbc:h2:mem:temp_flower_web;MVCC=TRUE");
props.setProperty(Environment.PASS, "");
props.setProperty(Environment.DIALECT, H2Dialect.class.getName());
props.setProperty(Environment.HBM2DDL_AUTO, "create");
// props.setProperty(Environment.SHOW_SQL, "true");
props.setProperty(PersistenceOptions.CASCADE_POLICY_ON_NON_CONTAINMENT, "REFRESH,PERSIST,MERGE");
props.setProperty(PersistenceOptions.PERSISTENCE_XML, "annotations.xml");
props.setProperty(PersistenceOptions.JOIN_TABLE_FOR_NON_CONTAINED_ASSOCIATIONS, "false");
props.setProperty(PersistenceOptions.ALWAYS_VERSION, "false");
// props.setProperty(PersistenceOptions.INHERITANCE_MAPPING, "TABLE_PER_CLASS");
props.setProperty(PersistenceOptions.INHERITANCE_MAPPING, "SINGLE_TABLE");
props.setProperty(PersistenceOptions.ADD_INDEX_FOR_FOREIGN_KEY, "false");
// create the HbDataStore using the name
final HbDataStore hbds = HbHelper.INSTANCE.createRegisterDataStore(DS_NAME);
// set the properties
hbds.setDataStoreProperties(props);
// sets its epackages stored in this datastore
hbds.setEPackages(new EPackage[] { EntityPackage.eINSTANCE, NotationPackage.eINSTANCE });
// ((HbSessionDataStore) hbds).setConfiguration(config);
// initialize
try {
hbds.initialize();
} catch (Throwable e) {
System.err.println(hbds.getMappingXML());
logger.error("FATAL: error while initializing the HbDataStore", e);
}
System.err.println(hbds.getMappingXML());
factory = hbds.getSessionFactory();
EventListenerRegistry registry = ((SessionFactoryImpl) factory).getServiceRegistry().getService(EventListenerRegistry.class);
SecurityEntityListener listener = new SecurityEntityListener();
registry.appendListeners(EventType.POST_INSERT, listener);
registry.appendListeners(EventType.POST_DELETE, listener);
registry.appendListeners(EventType.POST_UPDATE, listener);
registry.appendListeners(EventType.PRE_DELETE, listener);
initWithTestData();
new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
DBVersion dbVersion = null;
dbVersion = wrapper.find(DBVersion.class, DBVersion.SINGLETON_RECORD_ID);
if (dbVersion == null) {
logger.error("Cannot find database version from the database.");
} else if (dbVersion.getDbVersion() < FlowerProperties.DB_VERSION) {
logger.error("Database version mismatch. Database version = {} is lower than expected = {}. Please run the DB update scripts.", dbVersion.getDbVersion(), FlowerProperties.DB_VERSION);
} else if (dbVersion.getDbVersion() > FlowerProperties.DB_VERSION) {
logger.warn("Database version mismatch. Database version = {} is greater than expected = {}. You may continue, but please be aware that the application may malfunction and/or corrupt the data in the DB.", dbVersion.getDbVersion(), FlowerProperties.DB_VERSION);
} else {
// same version; everything is OK
logger.info("Database version check OK. Version = {}", dbVersion.getDbVersion());
}
}
});
}
public SessionFactory getFactory() {
return factory;
}
public void setFactory(SessionFactory factory) {
this.factory = factory;
}
/**
* Creates an organization with the given name, an admin, anonymous user, groups and permissions
* for the organization.
*
* @author Mariana
*/
private void createTestOrganization(final String organizationName, final String label, final String URL, final String logo) {
Subject subject = new Subject();
final Principal principal = new FlowerWebPrincipal(1); // FDC admin
subject.getPrincipals().add(principal);
Subject.doAsPrivileged(subject, new PrivilegedAction<Void>() {
@Override
public Void run() {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
// add org admin to DB
GeneralService service = new GeneralService();
User user = service.createUser(organizationName + "Admin", null, wrapper);
// add org to DB, add the user as admin and make sure the organization is not activated
Organization organization = service.createOrganization(organizationName, wrapper);
if (label != null) {
organization.setLabel(label);
}
organization.setURL(URL);
organization.setLogoURL(logo);
OrganizationUser ou = EntityFactory.eINSTANCE.createOrganizationUser();
ou.setUser(user);
ou.setOrganization(organization);
ou.setStatus(OrganizationMembershipStatus.ADMIN);
organization.setActivated(false);
// SVN test data
service.createSVNRepositoryURLAndAddToOrg("svn://csp1/flower2", organization);
service.createSVNCommentAndAddToUser("comment " + user.getName(), user);
wrapper.setOperationResult(OrganizationService.getInstance()
.convertOrganizationToOrganizationAdminUIDto(organization, user));
}
});
// approve org => will automatically create groups, users, permissions
// this is done outside of the session that create the organization and user, because this operation will try to find the objects in the DB
UserService.getInstance().approveDenyNewOrganization(null, (OrganizationAdminUIDto) wrapper.getOperationResult(), true, null);
return null;
}
}, null);
}
/**
* @author Cristi
* @author Mariana
*/
//TODO: eventually, this will have to be removed
protected void initWithTestData() {
// TODO CS/FP2 remove this
Resource hbResource = new HibernateResource(URI.createURI("hb:/?dsname=" + DS_NAME));
Diagram diagram = NotationFactory.eINSTANCE.createDiagram();
diagram.setName("D1");
diagram.setViewType("classDiagram");
hbResource.getContents().add(diagram);
Node node;
Bounds bounds;
node = NotationFactory.eINSTANCE.createNode();
node.setViewType("class");
diagram.getPersistentChildren().add(node);
bounds = NotationFactory.eINSTANCE.createBounds();
bounds.setX(120);
bounds.setY(10);
bounds.setWidth(100);
bounds.setHeight(100);
node.setLayoutConstraint(bounds);
node = NotationFactory.eINSTANCE.createNode();
node.setViewType("class");
diagram.getPersistentChildren().add(node);
bounds = NotationFactory.eINSTANCE.createBounds();
bounds.setX(10);
bounds.setY(10);
bounds.setWidth(100);
bounds.setHeight(100);
node.setLayoutConstraint(bounds);
try {
hbResource.save(Collections.EMPTY_MAP);
System.out.println("Saved diagram with id = " + hbResource.getURIFragment(diagram));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
hbResource.unload();
if ("false".equals(CommonPlugin.getInstance().getFlowerProperties().getProperty(PROP_DB_INIT_WITH_TEST_DATA))) {
return;
}
////////////////////////////////////////////////////////////////////////
// Initializations for all environments (production, test)
////////////////////////////////////////////////////////////////////////
new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
// db version
DBVersion dbVersion = EntityFactory.eINSTANCE.createDBVersion();
dbVersion.setId(DBVersion.SINGLETON_RECORD_ID);
dbVersion.setDbVersion(FlowerProperties.DB_VERSION);
wrapper.merge(dbVersion);
GeneralService service = new GeneralService();
// ALL group is not supposed to exist in the DB, it is a group where all users in the DB belong
Group all = EntityFactory.eINSTANCE.createGroup();
all.setName("ALL");
// everybody should be able to read .metadata
service.createPermission(FlowerWebFilePermission.class, ".metadata", all, FlowerWebFilePermission.READ_WRITE_DELETE, wrapper);
service.createPermission(FlowerWebFilePermission.class, ".metadata/*", all, FlowerWebFilePermission.READ_WRITE_DELETE, wrapper);
// this seems to be mandatory for SVN;
service.createPermission(FlowerWebFilePermission.class, "root", all, FlowerWebFilePermission.READ_WRITE, wrapper);
service.createPermission(FlowerWebFilePermission.class, "*", all, FlowerWebFilePermission.READ, wrapper);
// global admin group
Group fdc_admin = service.createGroup("fdc_admin", null, wrapper);
service.createUserAndAddToGroups("admin", null, Collections.singletonList(fdc_admin), wrapper);
service.createUserAndAddToGroups("anonymous", "anonymous", Collections.singletonList(fdc_admin), wrapper);
service.createPermission(AdminSecurityEntitiesPermission.class, "", fdc_admin, "*", wrapper);
// make sure that admin can create/edit/delete any permission
service.createPermission(ModifyTreePermissionsPermission.class, "*", fdc_admin, "*", wrapper);
service.createPermission(FlowerWebFilePermission.class, "*", fdc_admin, FlowerWebFilePermission.READ_WRITE_DELETE, wrapper);
}
});
if (!PROP_DB_INIT_WITH_TEST_DATA_INTERNAL.equals(CommonPlugin.getInstance().getFlowerProperties().getProperty(PROP_DB_INIT_WITH_TEST_DATA))) {
return;
}
////////////////////////////////////////////////////////////////////////
// Initializations for test environment only!!!
// These records don't exist in a production environment!!! Make sure that the
// app works correctly without them!
////////////////////////////////////////////////////////////////////////
createTestOrganization("org1", null, null, null);
createTestOrganization("org2", null, null, null);
createTestOrganization("crispico", null, null, null);
createTestOrganization("hibernate", "Hibernate", "http://www.hibernate.org/", "https://forum.hibernate.org/styles/hibernate/imageset/site_logo.gif");
createTestOrganization("spring", "Spring", "http://www.springsource.org/", "http://www.springsource.org/files/Logo_Spring_258x151.png");
createTestOrganization("flex", "Apache Flex", "http://flex.apache.org/", "http://flex.apache.org/images/logo_01_fullcolor-sm.png");
// TODO CS/FP2 remove this
new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
List<Organization> list = wrapper.findByField(Organization.class, "name", "hibernate");
WorkingDirectory wd;
wd = EntityFactory.eINSTANCE.createWorkingDirectory();
wd.setPathFromOrganization("git_repos");
wd.setColor(1);
wd.setOrganization(list.get(0));
wrapper.getSession().persist(wd);
wd = EntityFactory.eINSTANCE.createWorkingDirectory();
wd.setPathFromOrganization("git_repos_c");
wd.setColor(2);
wd.setOrganization(list.get(0));
wrapper.getSession().persist(wd);
}
});
new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
List<WorkingDirectory> list = wrapper.findAll(WorkingDirectory.class);
for (WorkingDirectory wd : list) {
System.out.println(wd.getPathFromOrganization());
}
}
});
}
}