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;
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: 
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: {
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,
sascha@1015:         Session                   session
sascha@1015:     ) {
sascha@1015:         session.doWork(new Work() {
sascha@1015:             @Override
sascha@1015:             public void execute(Connection systemConnection)
sascha@1015:             throws SQLException
sascha@1015:             {
sascha@1015:                 List<Builder.NamedConnection> connections =
sascha@1015:                     new ArrayList<Builder.NamedConnection>(2);
sascha@1015: 
sascha@1046:                 Connection userConnection = userId != null
sascha@1046:                     ? DBConfig
sascha@1015:                         .getInstance()
sascha@1015:                         .getDBConnection()
sascha@1046:                         .getDataSource()
sascha@1046:                         .getConnection()
sascha@1046:                     : null;
sascha@1015: 
sascha@1046:                 try {
sascha@1046:                     if (userConnection != null) {
sascha@1015:                         connections.add(new Builder.NamedConnection(
sascha@1022:                             Builder.CONNECTION_USER, userConnection, false));
sascha@1046:                     }
sascha@1015: 
sascha@1015:                     connections.add(new Builder.NamedConnection(
sascha@1022:                         Builder.CONNECTION_SYSTEM, systemConnection, true));
sascha@1015: 
sascha@1046:                     getBuilder().build(connections, result, parameters);
sascha@1046:                 }
sascha@1046:                 finally {
sascha@1046:                     if (userConnection != null) {
sascha@1046:                         userConnection.close();
sascha@1046:                     }
sascha@1015:                 }
sascha@1015:             }
sascha@1015:         });
sascha@1015:     }
sascha@1015: 
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 :