geoportal-logic/src/main/java/org/gcube/application/geoportal/managers/AbstractRecordManager.java

411 lines
12 KiB
Java

package org.gcube.application.geoportal.managers;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.sql.DataSource;
import org.gcube.application.geoportal.model.Record;
import org.gcube.application.geoportal.model.db.DBConstants;
import org.gcube.application.geoportal.model.db.DatabaseConnection;
import org.gcube.application.geoportal.model.db.PostgisTable;
import org.gcube.application.geoportal.model.db.PostgisTable.Field;
import org.gcube.application.geoportal.model.db.PostgisTable.FieldType;
import org.gcube.application.geoportal.model.fault.PersistenceException;
import org.gcube.application.geoportal.model.fault.PublishException;
import org.gcube.application.geoportal.model.fault.SDIInteractionException;
import org.gcube.application.geoportal.model.fault.ValidationException;
import org.gcube.application.geoportal.model.report.PublicationReport;
import org.gcube.application.geoportal.model.report.ValidationReport;
import org.gcube.application.geoportal.model.report.ValidationReport.ValidationStatus;
import org.gcube.application.geoportal.storage.ContentHandler;
import org.gcube.application.geoportal.storage.PostgisDBManager;
import org.gcube.application.geoportal.storage.PostgisDBManagerI;
import org.gcube.application.geoportal.utils.ISUtils;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.jpa.HibernatePersistenceProvider;
import jersey.repackaged.com.google.common.collect.ImmutableMap;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class AbstractRecordManager<T extends Record> {
public static interface EMFProvider {
public EntityManagerFactory getFactory();
}
public static void setDefaultProvider(EMFProvider provider) {
defaultProvider=provider;
}
private static EMFProvider defaultProvider=null;
private static EntityManagerFactory emf=null;
@Synchronized
protected static EntityManagerFactory getEMF() {
if(emf==null) {
try {
DatabaseConnection conn=ISUtils.queryForDB("postgresql", "internal-db");
log.debug("Found Internal Database : "+conn);
emf = new HibernatePersistenceProvider().createContainerEntityManagerFactory(
archiverPersistenceUnitInfo(),
ImmutableMap.<String, Object>builder()
.put(AvailableSettings.JPA_JDBC_DRIVER, "org.postgresql.Driver")
.put(AvailableSettings.JPA_JDBC_URL, conn.getUrl())
.put(AvailableSettings.DIALECT, org.hibernate.dialect.PostgreSQLDialect.class)
.put(AvailableSettings.HBM2DDL_AUTO, org.hibernate.tool.schema.Action.UPDATE)
.put(AvailableSettings.SHOW_SQL, true)
.put(AvailableSettings.QUERY_STARTUP_CHECKING, false)
.put(AvailableSettings.GENERATE_STATISTICS, false)
.put(AvailableSettings.USE_REFLECTION_OPTIMIZER, false)
.put(AvailableSettings.USE_SECOND_LEVEL_CACHE, false)
.put(AvailableSettings.USE_QUERY_CACHE, false)
.put(AvailableSettings.USE_STRUCTURED_CACHE, false)
.put(AvailableSettings.STATEMENT_BATCH_SIZE, 20)
.put(AvailableSettings.JPA_JDBC_USER, conn.getUser())
.put(AvailableSettings.JPA_JDBC_PASSWORD, conn.getPwd())
.build());
}catch(Throwable t) {
if(defaultProvider==null)
throw new RuntimeException("NO INTERNAL DADATABASE. Please contact VRE Manager (Required SE [platform : postgresq, 'GNA-DB' flag : internal-db])");
else {
log.warn("Found default provider. This should happen only in test phase.");
emf=defaultProvider.getFactory();
}
}
}
return emf;
}
@Synchronized
public static void shutdown() {
if(emf!=null) {
if(emf.isOpen()) emf.close();
emf=null;
}
}
public static Record getByID(long id) {
EntityManager em=getEMF().createEntityManager();
try {
log.debug("Looking for record by ID : "+id);
EntityTransaction tr=em.getTransaction();
tr.begin();
Record toReturn=em.find(Record.class, id);
log.debug("Loaded Record "+toReturn);
tr.commit();
return toReturn;
}finally {
if(em.isJoinedToTransaction())
em.flush();
em.close();
}
}
// protected T storeInfo()
// {
// log.debug("Storing Record "+theRecord);
// entityManager.persist(theRecord);
// return theRecord;
// }
//Transaction management
EntityTransaction transaction=null;
EntityManager entityManager=null;
//************************ INSTANCE
private T theRecord;
private ContentHandler<T> contentHandler;
protected AbstractRecordManager(T theRecord){
entityManager=getEMF().createEntityManager();
transaction=entityManager.getTransaction();
transaction.begin();
this.theRecord=theRecord;
// log.debug("Storing Record "+theRecord);
if(theRecord.getId()==0) {
log.debug("Passed record ID = 0. Assuming new ..");
entityManager.persist(theRecord);
}else {
log.debug("Passed record ID = "+theRecord.getId()+". Mergeing..");
entityManager.merge(theRecord);
}
this.contentHandler=new ContentHandler<T>(theRecord);
}
protected ContentHandler<T> getContentHandler() {
return contentHandler;
}
public T getRecord() {
return theRecord;
};
/**
* Commit to storages
*
* @return
* @throws PersistenceException
* @throws PublishException
*/
public T commit(boolean publish) throws PersistenceException,ValidationException, PublishException {
log.trace("Committing record "+theRecord+" Publish is "+publish);
ValidationReport report=theRecord.validate();
log.debug("Validated Report is "+report);
if(publish && report.getStatus().equals(ValidationStatus.ERROR))
throw new ValidationException(report,"Cannot publish project. See validation report");
// storeInfo();
log.debug("Record is valid, storing changed content");
contentHandler.storeChanges(publish);
// storeInfo();
if(publish) {
log.debug("Registering centroid of "+theRecord);
registerCentroid();
// storeInfo();
}
transaction.commit();
return theRecord;
}
public PublicationReport commitSafely(boolean publish) {
log.debug("Safely publishing "+theRecord);
PublicationReport toReturn=new PublicationReport("Publication Report");
toReturn.setTheRecord(getRecord());
ValidationReport validation=theRecord.validate();
validation.setObjectName("Validation report for "+validation.getObjectName());
if(validation.getStatus().equals(ValidationStatus.ERROR)) {
toReturn.addMessage(publish?ValidationStatus.ERROR:ValidationStatus.WARNING, "Record not valid.");
}
toReturn.addChild(validation);
log.debug("Record is valid, storing changed content [Publish is :"+publish+"]");
try {
toReturn.addChild(contentHandler.storeChanges(publish));
if(publish) {
log.debug("Registering centroid of "+theRecord);
registerCentroid();
toReturn.addMessage(ValidationStatus.PASSED, "Inserito centroide per record "+theRecord.getId());
}
} catch (PersistenceException e) {
toReturn.addChild(e.getReport());
} catch (PublishException e) {
toReturn.addMessage(ValidationStatus.WARNING, "Centroide non registrato");
}
try {
log.info("Report is "+toReturn.prettyPrint());
}catch (Exception e) {
log.warn("Unable to pretty print report "+toReturn,e);
}
return toReturn;
}
@Override
protected void finalize() throws Throwable {
if(transaction.isActive()) {
transaction.rollback();
}
entityManager.flush();
entityManager.close();
}
private void registerCentroid() throws PublishException {
try {
log.debug("Evaluating Centroid");
Map<String,String> centroidRow=evaluateCentroid();
log.debug("Contacting postgis DB .. ");
PostgisDBManagerI db=PostgisDBManager.get();
PostgisTable centroidsTable=getCentroidsTable();
log.debug("Inserting / updated centroid Row {} ",centroidRow);
PreparedStatement ps = db.prepareInsertStatement(centroidsTable, true, true);
log.debug("Deleting centroid if present. ID is "+theRecord.getId());
db.deleteByFieldValue(centroidsTable, new Field(DBConstants.Concessioni.PRODUCT_ID,FieldType.TEXT), theRecord.getId()+"");
centroidsTable.fillCSVPreparedStatament(centroidRow, ps, false);
ps.executeUpdate();
db.commit();
initCentroidLayer();
}catch(SQLException e) {
log.warn("Unable to publish Centroid for record "+theRecord,e);
throw new PublishException("Unable to publish centroid.",e, null);
}catch(SDIInteractionException e) {
log.warn("Unable to publish Centroid Layer for record type "+getRecord().getRecordType(),e);
throw new PublishException("Unable to publish centroid.",e, null);
}
}
protected abstract PostgisTable getCentroidsTable();
protected abstract void initCentroidLayer() throws SDIInteractionException;
protected abstract Map<String,String> evaluateCentroid();
//*********** PERSISTENCE
private static PersistenceUnitInfo archiverPersistenceUnitInfo() {
final List<String> MANAGED_CLASSES=Arrays.asList(new String[] {
"org.gcube.application.geoportal.model.Record",
"org.gcube.application.geoportal.model.concessioni.Concessione",
"org.gcube.application.geoportal.model.concessioni.LayerConcessione",
"org.gcube.application.geoportal.model.concessioni.RelazioneScavo",
"org.gcube.application.geoportal.model.content.AssociatedContent",
"org.gcube.application.geoportal.model.content.GeoServerContent",
"org.gcube.application.geoportal.model.content.OtherContent",
"org.gcube.application.geoportal.model.content.PersistedContent",
"org.gcube.application.geoportal.model.content.UploadedImage",
"org.gcube.application.geoportal.model.content.WorkspaceContent",
"org.gcube.application.geoportal.model.gis.ShapeFileLayerDescriptor",
"org.gcube.application.geoportal.model.gis.SDILayerDescriptor"});
return new PersistenceUnitInfo() {
@Override
public String getPersistenceUnitName() {
return "ApplicationPersistenceUnit";
}
@Override
public String getPersistenceProviderClassName() {
return "org.hibernate.jpa.HibernatePersistenceProvider";
}
@Override
public PersistenceUnitTransactionType getTransactionType() {
return PersistenceUnitTransactionType.RESOURCE_LOCAL;
}
@Override
public DataSource getJtaDataSource() {
return null;
}
@Override
public DataSource getNonJtaDataSource() {
return null;
}
@Override
public List<String> getMappingFileNames() {
return Collections.emptyList();
}
@Override
public List<URL> getJarFileUrls() {
try {
return Collections.list(this.getClass()
.getClassLoader()
.getResources(""));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
@Override
public URL getPersistenceUnitRootUrl() {
return null;
}
@Override
public List<String> getManagedClassNames() {
return MANAGED_CLASSES;
}
@Override
public boolean excludeUnlistedClasses() {
return true;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return null;
}
@Override
public ValidationMode getValidationMode() {
return null;
}
@Override
public Properties getProperties() {
return new Properties();
}
@Override
public String getPersistenceXMLSchemaVersion() {
return null;
}
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public void addTransformer(ClassTransformer transformer) {
}
@Override
public ClassLoader getNewTempClassLoader() {
return null;
}
};
}
}