sascha@1015: package de.intevation.flys.artifacts.datacage;
sascha@1015: 
sascha@1015: import java.util.Map;
sascha@1015: import java.util.HashMap;
sascha@1015: import java.util.List;
sascha@1015: import java.util.ArrayList;
sascha@1015: 
sascha@1015: import java.io.InputStream;
sascha@1015: import java.io.IOException;
sascha@1015: import java.io.File;
sascha@1015: 
sascha@1015: import java.io.FileInputStream;
sascha@1015: 
sascha@1015: import java.sql.Connection;
sascha@1015: import java.sql.SQLException;
sascha@1015: 
sascha@1015: import org.apache.log4j.Logger;
sascha@1015: 
sascha@1015: import org.w3c.dom.Document;
sascha@1015: import org.w3c.dom.Node;
sascha@1015: 
sascha@1015: import org.hibernate.Session;
teichmann@5590: import org.hibernate.SessionFactory;
sascha@1015: 
sascha@1015: import org.hibernate.jdbc.Work;
sascha@1015: 
sascha@1015: import de.intevation.artifacts.common.utils.Config;
sascha@1015: import de.intevation.artifacts.common.utils.XMLUtils;
sascha@1716: import de.intevation.artifacts.common.utils.StringUtils;
sascha@1015: 
sascha@1015: import de.intevation.flys.artifacts.FLYSArtifact;
sascha@1015: 
teichmann@5590: import de.intevation.flys.backend.SessionFactoryProvider;
sascha@1015: import de.intevation.flys.backend.SessionHolder;
sascha@1015: 
sascha@1015: import de.intevation.artifactdatabase.data.StateData;
sascha@1015: 
sascha@1015: import de.intevation.flys.artifacts.datacage.templating.Builder;
sascha@1015: 
felix@3391: 
felix@3391: /**
felix@3391:  * Also accessible as Singleton with getInstance().
felix@3391:  */
sascha@1015: public class Recommendations
sascha@1015: {
teichmann@5531:     public static final String CONNECTION_USER   = "user";
teichmann@5531:     public static final String CONNECTION_SYSTEM = "system";
teichmann@5531:     public static final String CONNECTION_SEDDB  = "seddb";
teichmann@5531: 
teichmann@5531:     public static final String DEFAULT_CONNECTION_NAME = CONNECTION_SYSTEM;
teichmann@5531: 
sascha@1015:     private static Logger log = Logger.getLogger(Recommendations.class);
sascha@1015: 
sascha@1030:     private static final boolean DEVELOPMENT_MODE =
sascha@1030:         Boolean.getBoolean("flys.datacage.recommendations.development");
sascha@1030: 
sascha@1046:     public static final String XPATH_TEMPLATE =
sascha@1046:         "/artifact-database/metadata/template/text()";
sascha@1015: 
sascha@1046:     public static final String DEFAULT_TEMPLATE_PATH =
sascha@1046:         "${artifacts.config.dir}/meta-data.xml";
sascha@1015: 
sascha@1015:     private static Recommendations INSTANCE;
sascha@1015: 
sascha@1030:     public static class BuilderProvider
sascha@1030:     {
sascha@1030:         protected Builder builder;
sascha@1030: 
sascha@1030:         public BuilderProvider() {
sascha@1030:         }
sascha@1030: 
sascha@1030:         public BuilderProvider(Builder builder) {
sascha@1030:             this.builder = builder;
sascha@1030:         }
sascha@1030: 
sascha@1030:         public Builder getBuilder() {
sascha@1030:             return builder;
sascha@1030:         }
sascha@1030:     } // class BuilderProvider
sascha@1030: 
sascha@1030:     public static class FileBuilderProvider
sascha@1030:     extends             BuilderProvider
sascha@1030:     {
sascha@1030:         protected File file;
sascha@1030:         protected long lastModified;
sascha@1030: 
sascha@1030:         public FileBuilderProvider() {
sascha@1030:         }
sascha@1030: 
sascha@1030:         public FileBuilderProvider(File file) {
sascha@1030:             this.file    = file;
sascha@1030:             lastModified = Long.MIN_VALUE;
sascha@1030:         }
sascha@1030: 
sascha@1030:         @Override
sascha@1030:         public synchronized Builder getBuilder() {
sascha@1030:             long modified = file.lastModified();
sascha@1030:             if (modified > lastModified) {
sascha@1030:                 lastModified = modified;
sascha@1030:                 try {
sascha@1030:                     Document template = loadTemplate(file);
sascha@1030:                     builder = new Builder(template);
sascha@1030:                 }
sascha@1030:                 catch (IOException ioe) {
sascha@1030:                     log.error(ioe);
sascha@1030:                 }
sascha@1030:             }
sascha@1030:             return builder;
sascha@1030:         }
sascha@1030: 
sascha@1030:         public BuilderProvider toStaticProvider() {
sascha@1030:             return new BuilderProvider(builder);
sascha@1030:         }
sascha@1030:     } // class BuilderProvider
sascha@1030: 
sascha@1046:     protected BuilderProvider builderProvider;
sascha@1015: 
sascha@1015:     public Recommendations() {
sascha@1015:     }
sascha@1015: 
sascha@1046:     public Recommendations(BuilderProvider builderProvider) {
sascha@1046:         this.builderProvider = builderProvider;
sascha@1015:     }
sascha@1015: 
sascha@1046:     public Builder getBuilder() {
sascha@1046:         return builderProvider.getBuilder();
sascha@1015:     }
sascha@1015: 
sascha@1015:     protected static void artifactToParameters(
sascha@3076:         FLYSArtifact        artifact,
sascha@1015:         Map<String, Object> parameters
sascha@1015:     ) {
sascha@1716:         parameters.put("CURRENT-STATE-ID", artifact.getCurrentStateId());
sascha@1716:         parameters.put("ARTIFACT-ID",      artifact.identifier());
sascha@1015: 
sascha@1015:         for (StateData sd: artifact.getAllData()) {
sascha@1015:             Object value = sd.getValue();
sascha@1015:             if (value == null) {
sascha@1015:                 continue;
sascha@1015:             }
sascha@1716:             String key = sd.getName().replace('.', '-').toUpperCase();
sascha@1015:             parameters.put(key, value);
sascha@1015:         }
sascha@1015:     }
sascha@1015: 
felix@4528: 
felix@4528:     /**
felix@4528:      * Put Key/Values from \param src to \param dst, but uppercase
felix@4528:      * both Keys and Values.
felix@4528:      */
sascha@1716:     public static void convertKeysToUpperCase(
sascha@1716:         Map<String, Object> src,
sascha@1716:         Map<String, Object> dst
sascha@1716:     ) {
sascha@1716:         for (Map.Entry<String, Object> entry: src.entrySet()) {
sascha@1716:             dst.put(entry.getKey().toUpperCase(), entry.getValue());
sascha@1716:         }
sascha@1716:     }
sascha@1716: 
felix@3391: 
felix@3391:     /**
felix@3391:      * Append recommendations to \param result.
felix@4528:      * @param extraParameters parameters (typicall example: 'recommended')
felix@3391:      */
sascha@1015:     public void  recommend(
sascha@1015:         FLYSArtifact        artifact,
sascha@1015:         String              userId,
sascha@1015:         String []           outs,
sascha@1015:         Map<String, Object> extraParameters,
sascha@1015:         Node                result
sascha@1015:     ) {
sascha@1015:         Map<String, Object> parameters = new HashMap<String, Object>();
sascha@1015: 
sascha@1015:         if (extraParameters != null) {
sascha@1716:             convertKeysToUpperCase(extraParameters, parameters);
sascha@1015:         }
sascha@1015: 
sascha@1015:         if (userId != null) {
sascha@1716:             parameters.put("USER-ID", userId);
sascha@1015:         }
sascha@1015: 
sascha@1015:         if (artifact != null) {
sascha@1015:             artifactToParameters(artifact, parameters);
sascha@1015:         }
sascha@1015: 
sascha@1716:         parameters.put("ARTIFACT-OUTS", StringUtils.toUpperCase(outs));
sascha@1015: 
sascha@1716:         parameters.put("PARAMETERS", parameters);
sascha@1015: 
sascha@1015:         recommend(parameters, userId, result);
sascha@1015:     }
sascha@1015: 
felix@3391: 
felix@3391:     /**
felix@3391:      * Append recommendations to \param result.
felix@3391:      */
sascha@1015:     public void recommend(
sascha@1015:         Map<String, Object> parameters,
sascha@1015:         String              userId,
sascha@1015:         Node                result
sascha@1015:     ) {
sascha@1015:         recommend(parameters, userId, result, SessionHolder.HOLDER.get());
sascha@1015:     }
sascha@1015: 
sascha@1015:     public void recommend(
sascha@1015:         final Map<String, Object> parameters,
sascha@1015:         final String              userId,
sascha@1015:         final Node                result,
teichmann@5531:         Session                   systemSession
sascha@1015:     ) {
teichmann@5531:         systemSession.doWork(new Work() {
sascha@1015:             @Override
teichmann@5531:             public void execute(final Connection systemConnection)
sascha@1015:             throws SQLException
sascha@1015:             {
teichmann@5590:                 SessionFactory sedDBFactory =
teichmann@5590:                     SessionFactoryProvider.getSedDBSessionFactory();
teichmann@5590: 
teichmann@5590:                 Session sedDBSession = sedDBFactory.openSession();
teichmann@5590:                 try {
teichmann@5590:                     sedDBSession.doWork(new Work() {
teichmann@5590:                         @Override
teichmann@5590:                         public void execute(Connection sedDBConnection)
teichmann@5590:                         throws SQLException
teichmann@5590:                         {
teichmann@5590:                             recommend(
teichmann@5590:                                 parameters, userId, result,
teichmann@5590:                                 systemConnection,
teichmann@5590:                                 sedDBConnection);
teichmann@5590:                         }
teichmann@5590:                     });
teichmann@5590:                 }
teichmann@5590:                 finally {
teichmann@5590:                     sedDBSession.close();
teichmann@5590:                 }
sascha@1015:             }
sascha@1015:         });
sascha@1015:     }
sascha@1015: 
teichmann@5531:     public void recommend(
teichmann@5531:         Map<String, Object> parameters,
teichmann@5531:         String              userId,
teichmann@5531:         Node                result,
teichmann@5531:         Connection          systemConnection,
teichmann@5531:         Connection          seddbConnection
teichmann@5531:     ) throws SQLException
teichmann@5531:     {
teichmann@5531:         List<Builder.NamedConnection> connections =
teichmann@5531:             new ArrayList<Builder.NamedConnection>(3);
teichmann@5531: 
teichmann@5531:         Connection userConnection = userId != null
teichmann@5531:             ? DBConfig
teichmann@5531:                 .getInstance()
teichmann@5531:                 .getDBConnection()
teichmann@5531:                 .getDataSource()
teichmann@5531:                 .getConnection()
teichmann@5531:             : null;
teichmann@5531: 
teichmann@5531:         try {
teichmann@5531:             connections.add(new Builder.NamedConnection(
teichmann@5531:                 CONNECTION_SYSTEM, systemConnection, true));
teichmann@5531: 
teichmann@5588:             if (seddbConnection != null) {
teichmann@5588:                 connections.add(new Builder.NamedConnection(
teichmann@5588:                     CONNECTION_SEDDB, seddbConnection, true));
teichmann@5588:             }
teichmann@5531: 
teichmann@5531:             if (userConnection != null) {
teichmann@5531:                 connections.add(new Builder.NamedConnection(
teichmann@5531:                     CONNECTION_USER, userConnection, false));
teichmann@5531:             }
teichmann@5531: 
teichmann@5531: 
teichmann@5531:             getBuilder().build(connections, result, parameters);
teichmann@5531:         }
teichmann@5531:         finally {
teichmann@5531:             if (userConnection != null) {
teichmann@5531:                 userConnection.close();
teichmann@5531:             }
teichmann@5531:         }
teichmann@5531:     }
felix@3391: 
felix@3391:     /** Get singleton instance. */
sascha@1015:     public static synchronized Recommendations getInstance() {
sascha@1015:         if (INSTANCE == null) {
sascha@1015:             INSTANCE = createRecommendations();
sascha@1015:         }
sascha@1015:         return INSTANCE;
sascha@1015:     }
sascha@1015: 
felix@3391: 
sascha@1015:     protected static Document loadTemplate(File file) throws IOException {
sascha@1015:         InputStream in = null;
sascha@1015: 
sascha@1015:         try {
sascha@1015:             in = new FileInputStream(file);
sascha@1015: 
sascha@1015:             Document template = XMLUtils.parseDocument(in);
sascha@1015: 
sascha@1015:             if (template == null) {
sascha@1015:                 throw new IOException("cannot load template");
sascha@1015:             }
sascha@1015:             return template;
sascha@1015:         }
sascha@1015:         finally {
sascha@1015:             if (in != null) {
sascha@1015:                 try {
sascha@1015:                     in.close();
sascha@1015:                 }
sascha@1015:                 catch (IOException ioe) {
sascha@1015:                     log.error(ioe);
sascha@1015:                 }
sascha@1015:             }
sascha@1015:         }
sascha@1015:     }
sascha@1015: 
sascha@1046:     public static Recommendations createRecommendations(File file) {
sascha@1015:         log.debug("Recommendations.createBuilder");
sascha@1015: 
sascha@1046:         if (!file.isFile() || !file.canRead()) {
sascha@1046:             log.error("Cannot open template file '" + file + "'");
sascha@1015:             return null;
sascha@1015:         }
sascha@1015: 
sascha@1046:         FileBuilderProvider fbp = new FileBuilderProvider(file);
sascha@1015: 
sascha@1046:         if (fbp.getBuilder() == null) {
sascha@1030:             log.error("failed loading builder");
sascha@1015:             return null;
sascha@1015:         }
sascha@1030: 
sascha@1046:         BuilderProvider bp = DEVELOPMENT_MODE
sascha@1046:             ? fbp
sascha@1046:             : fbp.toStaticProvider();
sascha@1030: 
sascha@1046:         return new Recommendations(bp);
sascha@1015:     }
sascha@1015: 
sascha@1015:     protected static Recommendations createRecommendations() {
sascha@1015:         log.debug("Recommendations.createRecommendations");
sascha@1015: 
sascha@1046:         String path = Config.getStringXPath(XPATH_TEMPLATE);
sascha@1015: 
sascha@1046:         if (path == null) {
sascha@1046:             path = DEFAULT_TEMPLATE_PATH;
sascha@1015:         }
sascha@1015: 
sascha@1046:         path = Config.replaceConfigDir(path);
sascha@1015: 
sascha@1046:         log.info("Meta data template: " + path);
sascha@1046: 
sascha@1046:         return createRecommendations(new File(path));
sascha@1015:     }
sascha@1015: }
sascha@1015: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :