Package it.eng.spagobi.tools.scheduler.jobs

Source Code of it.eng.spagobi.tools.scheduler.jobs.ExecuteBIDocumentJob$SchedulerDataSource

/**

SpagoBI - The Business Intelligence Free Platform

Copyright (C) 2005-2008 Engineering Ingegneria Informatica S.p.A.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

**/
package it.eng.spagobi.tools.scheduler.jobs;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.validator.GenericValidator;
import org.apache.log4j.Logger;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;


import it.eng.spago.error.EMFUserError;
import it.eng.spago.security.IEngUserProfile;
import it.eng.spagobi.analiticalmodel.document.bo.BIObject;
import it.eng.spagobi.analiticalmodel.document.bo.ObjTemplate;
import it.eng.spagobi.analiticalmodel.document.bo.Snapshot;
import it.eng.spagobi.analiticalmodel.document.dao.IBIObjectDAO;
import it.eng.spagobi.analiticalmodel.document.dao.ISnapshotDAO;
import it.eng.spagobi.analiticalmodel.document.handlers.ExecutionController;
import it.eng.spagobi.analiticalmodel.functionalitytree.bo.LowFunctionality;
import it.eng.spagobi.behaviouralmodel.analyticaldriver.bo.BIObjectParameter;
import it.eng.spagobi.behaviouralmodel.analyticaldriver.bo.ParameterValuesRetriever;
import it.eng.spagobi.behaviouralmodel.check.bo.Check;
import it.eng.spagobi.commons.SingletonConfig;
import it.eng.spagobi.commons.bo.Domain;
import it.eng.spagobi.commons.bo.UserProfile;
import it.eng.spagobi.commons.dao.DAOFactory;
import it.eng.spagobi.commons.dao.IDomainDAO;
import it.eng.spagobi.commons.utilities.ExecutionProxy;
import it.eng.spagobi.commons.utilities.GeneralUtilities;
import it.eng.spagobi.commons.utilities.ObjectsAccessVerifier;
import it.eng.spagobi.commons.utilities.StringUtilities;
import it.eng.spagobi.commons.utilities.messages.IMessageBuilder;
import it.eng.spagobi.commons.utilities.messages.MessageBuilderFactory;
import it.eng.spagobi.engines.config.bo.Engine;
import it.eng.spagobi.engines.config.dao.IEngineDAO;
import it.eng.spagobi.events.EventsManager;
import it.eng.spagobi.tools.dataset.bo.IDataSet;
import it.eng.spagobi.tools.dataset.common.behaviour.UserProfileUtils;
import it.eng.spagobi.tools.dataset.common.datastore.IDataStore;
import it.eng.spagobi.tools.dataset.common.datastore.IField;
import it.eng.spagobi.tools.dataset.common.datastore.IRecord;
import it.eng.spagobi.tools.distributionlist.bo.DistributionList;
import it.eng.spagobi.tools.distributionlist.bo.Email;
import it.eng.spagobi.tools.scheduler.Formula;
import it.eng.spagobi.tools.scheduler.FormulaParameterValuesRetriever;
import it.eng.spagobi.tools.scheduler.RuntimeLoadingParameterValuesRetriever;
import it.eng.spagobi.tools.scheduler.to.SaveInfo;
import it.eng.spagobi.tools.scheduler.utils.BIObjectParametersIterator;
import it.eng.spagobi.tools.scheduler.utils.JavaClassDestination;
import it.eng.spagobi.tools.scheduler.utils.SchedulerUtilities;


public class ExecuteBIDocumentJob implements Job {

  static private Logger logger = Logger.getLogger(ExecuteBIDocumentJob.class)

  public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    IEngUserProfile profile;
    JobDataMap jobDataMap;
   
    // documentLabel__num this is necessary because the same document can be added to one scheduled activity more than one time
    String documentInstanceName;
    String documentLabel;
   
    // par1=val1&par2=val2... for parameters already set in scheduled activity's configuration
    String inputParametersQueryString;
   
    IBIObjectDAO biobjdao;
    BIObject biobj;
    ExecutionController executionController;
    ExecutionProxy executionProxy;
    EventsManager eventManager;
   
    logger.debug("IN");
   
    try {
      profile = UserProfile.createSchedulerUserProfile();
      jobDataMap = jobExecutionContext.getMergedJobDataMap();
      biobjdao = DAOFactory.getBIObjectDAO();
     
      String doclabelsConcat = jobDataMap.getString("documentLabels");
      String[] docLabels = doclabelsConcat.split(",");
      Iterator itr = jobDataMap.keySet().iterator();
      while(itr.hasNext()) {
        Object key = itr.next();
        Object value = jobDataMap.get(key);
        logger.debug("jobDataMap parameter [" + key + "] is equal to [" + value + "]");
      }
     
      long startSchedule = System.currentTimeMillis();
      logger.debug("Scheduled activity contains [" + docLabels.length + "] documnt(s)");

      for(int ind = 0; ind < docLabels.length; ind++) {
        documentInstanceName = docLabels[ind];
        documentLabel = documentInstanceName.substring(0, documentInstanceName.lastIndexOf("__"));
        logger.debug("Processing document [" + (ind+1) + "] with label [" + documentLabel + "] ...");
       
        inputParametersQueryString = jobDataMap.getString(documentInstanceName);
        logger.debug("Input parameters query string for documet [" + documentLabel + "] is equal to [" + inputParametersQueryString + "]");
       
        // load bidocument
        biobj = biobjdao.loadBIObjectByLabel(documentLabel);
       
        // get the save options
        String saveOptString = jobDataMap.getString("biobject_id_" + biobj.getId() + "__"+ (ind+1));
        SaveInfo saveInfo = SchedulerUtilities.fromSaveInfoString(saveOptString);
       
        // create the execution controller
        executionController = new ExecutionController();
        executionController.setBiObject(biobj);
       
        // fill parameters
        executionController.refreshParameters(biobj, inputParametersQueryString);

        String iterativeParametersString = jobDataMap.getString(documentInstanceName + "_iterative");
        logger.debug("Iterative parameter configuration for documet [" + documentLabel + "] is equal to [" + iterativeParametersString + "]");
        setIterativeParameters(biobj, iterativeParametersString);
       
        String loadAtRuntimeParametersString = jobDataMap.getString(documentInstanceName + "_loadAtRuntime");
        logger.debug("Runtime parameter configuration for documet [" + documentLabel + "] is equal to [" + loadAtRuntimeParametersString + "]");
        setLoadAtRuntimeParameters(biobj, loadAtRuntimeParametersString);
       
        String useFormulaParametersString = jobDataMap.getString(documentInstanceName + "_useFormula");
        logger.debug("Formuula based parameter configuration for documet [" + documentLabel + "] is equal to [" + useFormulaParametersString + "]");
        setUseFormulaParameters(biobj, useFormulaParametersString);

        retrieveParametersValues(biobj);

        //gets the dataset data about the email address
        IDataStore emailDispatchDataStore = null;
        if (saveInfo.isUseDataSet()) {
          IDataSet dataSet = DAOFactory.getDataSetDAO().loadActiveDataSetByLabel(saveInfo.getDataSetLabel());
          dataSet.setUserProfileAttributes(UserProfileUtils.getProfileAttributes(profile));
          dataSet.loadData();
          emailDispatchDataStore = dataSet.getDataStore();
        }
        //gets the dataset data about the folder for the document save
        IDataStore folderDispatchDataSotre = null;
        if (saveInfo.isUseFolderDataSet()) {
          IDataSet dataSet = DAOFactory.getDataSetDAO().loadActiveDataSetByLabel(saveInfo.getDataSetFolderLabel());
            dataSet.setUserProfileAttributes(UserProfileUtils.getProfileAttributes(profile));
          dataSet.loadData();
           folderDispatchDataSotre = dataSet.getDataStore();
        }
       
        eventManager = EventsManager.getInstance();
        List roles = DAOFactory.getBIObjectDAO().getCorrectRolesForExecution(biobj.getId());
       
        String startExecMsg = "${scheduler.startexecsched} " + biobj.getName()
        Integer idEvent = eventManager.registerEvent("Scheduler", startExecMsg, "", roles);

       
        Map tempParMap = new HashMap();
        BIObjectParametersIterator objectParametersIterator = new BIObjectParametersIterator(biobj.getBiObjectParameters());
        while (objectParametersIterator.hasNext()) {
          List parameters = (List) objectParametersIterator.next();
          biobj.setBiObjectParameters(parameters);
       

          StringBuffer toBeAppendedToName = new StringBuffer();
          StringBuffer toBeAppendedToDescription = new StringBuffer(" [");
          Iterator parametersIt = parameters.iterator();
          while (parametersIt.hasNext()) {
           
            BIObjectParameter aParameter = (BIObjectParameter) parametersIt.next();
           
            tempParMap.put(aParameter.getParameterUrlName(), aParameter.getParameterValuesAsString());
            if (aParameter.isIterative()) {
              toBeAppendedToName.append("_" + aParameter.getParameterValuesAsString());
              toBeAppendedToDescription.append(aParameter.getLabel() + ":" + aParameter.getParameterValuesAsString() + "; ");
            }
          }
          // if there are no iterative parameters, toBeAppendedToDescription is " [" and must be cleaned
          if (toBeAppendedToDescription.length() == 2) {
            toBeAppendedToDescription.delete(0, 2);
          } else {
            // toBeAppendedToDescription ends with "; " and must be cleaned
            toBeAppendedToDescription.delete(toBeAppendedToDescription.length() - 2, toBeAppendedToDescription.length());
            toBeAppendedToDescription.append("]");
          }

          // appending the current date
          Date date = new Date();
          SimpleDateFormat sdf = new SimpleDateFormat();
          sdf.applyPattern("dd:MM:yyyy");
          String dateStr = sdf.format(date);
          toBeAppendedToName.append("_" + dateStr);
         
         

          //check parameters value: if a parameter hasn't value but isn't mandatory the process
          //must go on and so hasValidValue is set to true
          List tmpBIObjectParameters = biobj.getBiObjectParameters();
          Iterator it = tmpBIObjectParameters.iterator();
          while (it.hasNext()){
            boolean isMandatory = false;
            BIObjectParameter aBIObjectParameter = (BIObjectParameter)it.next();
            List checks = aBIObjectParameter.getParameter().getChecks();
            if (checks != null && !checks.isEmpty()) {
              Iterator checksIt = checks.iterator();
              while (checksIt.hasNext()) {
                Check check = (Check) checksIt.next();
                if (check.getValueTypeCd().equalsIgnoreCase("MANDATORY")) {
                  isMandatory = true;
                  break;
                }
              }
            }
            if (!isMandatory &&
                (aBIObjectParameter.getParameterValues() == null  || aBIObjectParameter.getParameterValues().size() == 0)) {
              aBIObjectParameter.setParameterValues(new ArrayList());
              aBIObjectParameter.setHasValidValues(true);
            }
          }


          // exec the document only if all its parameter are filled
          if(executionController.directExecution()) {
           
            logger.debug("Save as snapshot is eual to [" + saveInfo.isSaveAsSnapshot() + "]");
            logger.debug("Dispatch to a distribution list is eual to [" + saveInfo.isSendToDl() + "]");
            logger.debug("Dispatch to a java class is eual to [" + saveInfo.isSendToJavaClass() + "]");
            logger.debug("Dispatch by mail-list is eual to [" + saveInfo.isSendMail() + "]");
            logger.debug("Dispatch by folder-list is eual to [" + saveInfo.isSaveAsDocument() + "]");
           
            if(!saveInfo.isSaveAsSnapshot() && !saveInfo.isSendToDl() && !saveInfo.isSendToJavaClass()) {
              boolean noValidDispatchTarget = false;
              if(saveInfo.isSendMail()) {
                String[] recipients = findRecipients(saveInfo, biobj, emailDispatchDataStore);
                if (recipients != null && recipients.length > 0) {
                  noValidDispatchTarget = false;
                  logger.debug("Found at least one target of type mail");
                }else{
                  noValidDispatchTarget = true;
                }
              }
             
              if(saveInfo.isSaveAsDocument()) {
                List storeInFunctionalities = findFolders(saveInfo, biobj, folderDispatchDataSotre);
                if(storeInFunctionalities != null && !storeInFunctionalities.isEmpty()) {
                  noValidDispatchTarget = false;
                  logger.debug("Found at least one target of type folder");
                }else{
                  noValidDispatchTarget = true;
                }
              }
             
              if(noValidDispatchTarget) {
                logger.debug("No valid dispatch target for document [" + (ind+1) + "] with label [" + documentInstanceName + "] and parameters [" + toBeAppendedToDescription +"]");
                logger.info("Document [" + (ind+1) + "] with label [" + documentInstanceName + "] and parameters " + toBeAppendedToDescription + " not executed: no valid dispatch target");
                continue;
              } else if(!saveInfo.isSaveAsDocument() && !saveInfo.isSendMail()){
                logger.debug("There are no dispatch targets for document with label [" + documentInstanceName + "] - if not an ETL, WEKA or KPI document a dispatch target should be added");
              }else{
                logger.debug("There is at list one dispatch target for document with label [" + documentInstanceName + "]");
              }
            }

            executionProxy = new ExecutionProxy();
            executionProxy.setBiObject(biobj);
           
           
           
           
            logger.info("Executing document [" + (ind+1) + "] with label [" + documentInstanceName + "] and parameters " + toBeAppendedToDescription +" ...");
            long start = System.currentTimeMillis();
            byte[] response = executionProxy.exec(profile, "SCHEDULATION", null);
            if (response == null || response.length == 0) {
              logger.debug("Document executed without any response");
            }
            String retCT = executionProxy.getReturnedContentType();
            String fileextension = executionProxy.getFileExtensionFromContType(retCT);
            long end = System.currentTimeMillis();     
            long elapsed = (end - start)/1000;
            logger.info("Document [" + (ind+1) + "] with label [" + documentInstanceName + "] and parameters " + toBeAppendedToDescription +" executed in [" + elapsed + "]");
           
           
            if(saveInfo.isSaveAsSnapshot()) {
              saveAsSnap(saveInfo, biobj, response, toBeAppendedToName.toString(), toBeAppendedToDescription.toString(),profile);
            }

            if(saveInfo.isSaveAsDocument()) {
              saveAsDocument(saveInfo, biobj,jobExecutionContext, response, fileextension, folderDispatchDataSotre, toBeAppendedToName.toString(), toBeAppendedToDescription.toString());
            }

            if(saveInfo.isSendMail()) {
              sendMail(saveInfo, biobj, tempParMap, response, retCT, fileextension, emailDispatchDataStore, toBeAppendedToName.toString(), toBeAppendedToDescription.toString());
            }
            if(saveInfo.isSendToDl()) {
              sendToDl(saveInfo, biobj, response, retCT, fileextension, toBeAppendedToName.toString(), toBeAppendedToDescription.toString());
              if(jobExecutionContext.getNextFireTime()== null){
                String triggername = jobExecutionContext.getTrigger().getName();
                List dlIds = saveInfo.getDlIds();
                it = dlIds.iterator();
                while(it.hasNext()){
                  Integer dlId = (Integer)it.next();
                  DistributionList dl = DAOFactory.getDistributionListDAO().loadDistributionListById(dlId);
                  DAOFactory.getDistributionListDAO().eraseDistributionListObjects(dl, (biobj.getId()).intValue(), triggername);
                }
              }
            }

            if(saveInfo.isSendToJavaClass()) {
              sendToJavaClass(saveInfo, biobj, response);
            }


          } else {
            logger.warn("The document with label "+documentInstanceName+" cannot be executed directly, " +
            "maybe some prameters are not filled ");
            throw new Exception("The document with label "+documentInstanceName+" cannot be executed directly, " +
            "maybe some prameters are not filled ");
          }
        }
       
        String endExecMsg = "${scheduler.endexecsched} " + biobj.getName();
        eventManager.registerEvent("Scheduler", endExecMsg, "", roles);

      }

     
      long endSchedule = System.currentTimeMillis();
      long elapsedSchedule = (endSchedule-startSchedule)/1000;
      logger.info("Scheduled activity succesfully ended in [" + elapsedSchedule +"] sec.");
    } catch (Exception e) {
      logger.error("Error while executiong job ", e);
    } finally {
      logger.debug("OUT");
    }
  }




  private void retrieveParametersValues(BIObject biobj) throws Exception {
    logger.debug("IN");
    try {
      List parameters = biobj.getBiObjectParameters();
      if (parameters == null || parameters.isEmpty()) {
        logger.debug("Document has no parameters");
        return;
      }
      Iterator it = parameters.iterator();
      while (it.hasNext()) {
        BIObjectParameter parameter = (BIObjectParameter) it.next();
        ParameterValuesRetriever retriever = parameter.getParameterValuesRetriever();
        if (retriever != null) {
          logger.debug("Document parameter with url name [" + parameter.getParameterUrlName() + "] has a parameter values retriever: " + retriever);
          logger.debug("Retrieving values...");
          List<String> values = null;
          try {
            values = retriever.retrieveValues(parameter);
          } catch (Exception e) {
            logger.error("Error while retrieving values for parameter with url name [" + parameter.getParameterUrlName() + "] of document [" + biobj.getLabel() + "].", e);
            throw e;
          }
          logger.debug("Values retrieved.");
          parameter.setParameterValues(values);
          parameter.setTransientParmeters(true);
        }
      }
    } finally {
      logger.debug("OUT");
    }
  }




  private void setLoadAtRuntimeParameters(BIObject biobj, String loadAtRuntimeParametersString) {
    logger.debug("IN");
    try {
      List parameters = biobj.getBiObjectParameters();
      if (parameters == null || parameters.isEmpty()) {
        logger.debug("Document has no parameters");
        return;
      }
      if (loadAtRuntimeParametersString == null || loadAtRuntimeParametersString.trim().trim().equals("")) {
        logger.debug("No load-at-runtime parameters found");
        return;
      }
      String[] loadAtRuntimeParameters = loadAtRuntimeParametersString.split(";");

      Map<String, String> loadAtRuntimeParametersMap = new HashMap<String, String>();
      for (int count = 0; count < loadAtRuntimeParameters.length; count++) {
        String loadAtRuntime = loadAtRuntimeParameters[count];
        int parameterUrlNameIndex = loadAtRuntime.lastIndexOf("(");
        String parameterUrlName = loadAtRuntime.substring(0, parameterUrlNameIndex);
        String userAndRole = loadAtRuntime.substring(parameterUrlNameIndex + 1, loadAtRuntime.length() - 1);
        loadAtRuntimeParametersMap.put(parameterUrlName, userAndRole);
      }

      Iterator it = parameters.iterator();
      while (it.hasNext()) {
        BIObjectParameter parameter = (BIObjectParameter) it.next();
        if (loadAtRuntimeParametersMap.containsKey(parameter.getParameterUrlName())) {
          logger.debug("Document parameter with url name [" + parameter.getParameterUrlName() + "] was configured to be calculated at runtime.");
          RuntimeLoadingParameterValuesRetriever strategy = new RuntimeLoadingParameterValuesRetriever();
          String userRoleStr = loadAtRuntimeParametersMap.get(parameter.getParameterUrlName());
          String[] userRole = userRoleStr.split("\\|");
          strategy.setUserIndentifierToBeUsed(userRole[0]);
          strategy.setRoleToBeUsed(userRole[1]);
          parameter.setParameterValuesRetriever(strategy);
        }
      }
    } finally {
      logger.debug("OUT");
    }
  }




  private void setIterativeParameters(BIObject biobj, String iterativeParametersString) {
    logger.debug("IN");
    try {
      List parameters = biobj.getBiObjectParameters();
      if (parameters == null || parameters.isEmpty()) {
        logger.debug("Document has no parameters");
        return;
      }
      if (iterativeParametersString == null || iterativeParametersString.trim().trim().equals("")) {
        logger.debug("No iterative parameters found");
        return;
      }
      String[] iterativeParameters = iterativeParametersString.split(";");
      List iterativeParametersList = Arrays.asList(iterativeParameters);
      Iterator it = parameters.iterator();
      while (it.hasNext()) {
        BIObjectParameter parameter = (BIObjectParameter) it.next();
        parameter.setIterative(false);
        if (iterativeParametersList.contains(parameter.getParameterUrlName())) {
          logger.debug("Document parameter with url name [" + parameter.getParameterUrlName() + "] was configured to be iterative.");
          parameter.setIterative(true);
        }
      }
    } finally {
      logger.debug("OUT");
    }
  }

  private void setUseFormulaParameters(BIObject biobj, String useFormulaParametersString) {
    logger.debug("IN");
    try {
      List parameters = biobj.getBiObjectParameters();
      if (parameters == null || parameters.isEmpty()) {
        logger.debug("Document has no parameters");
        return;
      }
      if (useFormulaParametersString == null || useFormulaParametersString.trim().trim().equals("")) {
        logger.debug("No parameters using formula found");
        return;
      }

      String[] useFormulaParameters = useFormulaParametersString.split(";");

      Map<String, String> useFormulaParametersMap = new HashMap<String, String>();
      for (int count = 0; count < useFormulaParameters.length; count++) {
        String useFormula = useFormulaParameters[count];
        int parameterUrlNameIndex = useFormula.lastIndexOf("(");
        String parameterUrlName = useFormula.substring(0, parameterUrlNameIndex);
        String userAndRole = useFormula.substring(parameterUrlNameIndex + 1, useFormula.length() - 1);
        useFormulaParametersMap.put(parameterUrlName, userAndRole);
      }

      Iterator it = parameters.iterator();
      while (it.hasNext()) {
        BIObjectParameter parameter = (BIObjectParameter) it.next();
        if (useFormulaParametersMap.containsKey(parameter.getParameterUrlName())) {
          logger.debug("Document parameter with url name [" + parameter.getParameterUrlName() + "] was configured to use a formula.");
          FormulaParameterValuesRetriever strategy = new FormulaParameterValuesRetriever();
          String fName = useFormulaParametersMap.get(parameter.getParameterUrlName());
          Formula f = Formula.getFormula(fName);
          strategy.setFormula(f);
          parameter.setParameterValuesRetriever(strategy);
        }
      }
    } finally {
      logger.debug("OUT");
    }
  }


  private void sendToJavaClass(SaveInfo sInfo,BIObject biobj, byte[] response) throws Exception {
    logger.debug("IN");

    String javaClass = sInfo.getJavaClassPath();
    if( (javaClass==null) || javaClass.trim().equals("")) {
      logger.error("Classe java nons specificata");
      return;
    }
    // try to get new Instance
    JavaClassDestination jcDest=null;
    try{
      jcDest=(JavaClassDestination)Class.forName(javaClass).newInstance();
    }
    catch (ClassCastException e) {
      logger.error("Class "+javaClass+" does not extend JavaClassDestination class as expected");
      return;
    }
    catch (Exception e) {
      logger.error("Error while instantiating the class "+javaClass);
      return;
      }

    logger.debug("Sucessfull instantiation of "+javaClass);

    jcDest.setBiObj(biobj);
    jcDest.setDocumentByte(response);

    try{
      jcDest.execute();
    }
    catch (Exception e) {
      logger.error("Error during execution",e);
      return;
    }


    logger.debug("OUT");


  }


  private void saveAsSnap(SaveInfo sInfo,BIObject biobj, byte[] response, String toBeAppendedToName, String toBeAppendedToDescription,IEngUserProfile profile) {
    logger.debug("IN");
    try {
      String snapName = sInfo.getSnapshotName();
      if( (snapName==null) || snapName.trim().equals("")) {
        throw new Exception("Document name not specified");
      }
      //snapName += toBeAppendedToName;
      if (snapName.length() > 100) {
        logger.warn("Snapshot name [" + snapName + "] exceeds maximum length that is 100, it will be truncated");
        snapName = snapName.substring(0, 100);
      }

      String snapDesc = sInfo.getSnapshotDescription() != null ? sInfo.getSnapshotDescription() : "";
      snapDesc += toBeAppendedToDescription;
      if (snapDesc.length() > 1000) {
        logger.warn("Snapshot description [" + snapDesc + "] exceeds maximum length that is 1000, it will be truncated");
        snapDesc = snapDesc.substring(0, 1000);
      }

      String historylengthStr = sInfo.getSnapshotHistoryLength();
      // store document as snapshot
      ISnapshotDAO snapDao = DAOFactory.getSnapshotDAO();
      snapDao.setUserProfile(profile);
      // get the list of snapshots
      List allsnapshots = snapDao.getSnapshots(biobj.getId());
      // get the list of the snapshot with the store name
      List snapshots = SchedulerUtilities.getSnapshotsByName(allsnapshots, snapName);
      // get the number of previous snapshot saved
      int numSnap = snapshots.size();
      // if the number of snapshot is greater or equal to the history length then
      // delete the unecessary snapshots
      if((historylengthStr!=null) && !historylengthStr.trim().equals("")){
        try{
          Integer histLenInt = new Integer(historylengthStr);
          int histLen = histLenInt.intValue();
          if(numSnap>=histLen){
            int delta = numSnap - histLen;
            for(int i=0; i<=delta; i++) {
              Snapshot snap = SchedulerUtilities.getNamedHistorySnapshot(allsnapshots, snapName, histLen-1);
              Integer snapId = snap.getId();
              snapDao.deleteSnapshot(snapId);
            }
          }
        } catch(Exception e) {
          logger.error("Error while deleting object snapshots", e);
        }
      }
      snapDao.saveSnapshot(response, biobj.getId(), snapName, snapDesc)
    } catch (Exception e) {
      logger.error("Error while saving schedule result as new snapshot", e);
    }finally{
      logger.debug("OUT");
    }
  }





  private void saveAsDocument(SaveInfo sInfo,BIObject biobj, JobExecutionContext jex, byte[] response, String fileExt, IDataStore dataStore, String toBeAppendedToName, String toBeAppendedToDescription) {
    logger.debug("IN");
    try{
      String docName = sInfo.getDocumentName();
      if( (docName==null) || docName.trim().equals("")) {
        throw new Exception(" Document name not specified");
      }
      docName += toBeAppendedToName;
      String docDesc = sInfo.getDocumentDescription() + toBeAppendedToDescription;

      // recover office document sbidomains
      IDomainDAO domainDAO = DAOFactory.getDomainDAO();
      Domain officeDocDom = domainDAO.loadDomainByCodeAndValue("BIOBJ_TYPE", "OFFICE_DOC");
      // recover development sbidomains
      Domain relDom = domainDAO.loadDomainByCodeAndValue("STATE", "REL");
      // recover engine
      IEngineDAO engineDAO = DAOFactory.getEngineDAO();
      List engines = engineDAO.loadAllEnginesForBIObjectType(officeDocDom.getValueCd());
      if(engines.isEmpty()) {
        throw new Exception(" No suitable engines for the new document");
      }
      Engine engine = (Engine)engines.get(0);   
      // load the template
      ObjTemplate objTemp = new ObjTemplate();
      objTemp.setActive(new Boolean(true));
      objTemp.setContent(response);
      objTemp.setName(docName + fileExt);
      // load all functionality
      /*orig
      List storeInFunctionalities = new ArrayList();
      String functIdsConcat = sInfo.getFunctionalityIds();
      String[] functIds =  functIdsConcat.split(",");
      for(int i=0; i<functIds.length; i++) {
        String functIdStr = functIds[i];
        if(functIdStr.trim().equals(""))
          continue;
        Integer functId = Integer.valueOf(functIdStr);
        storeInFunctionalities.add(functId);
      }*/
      List storeInFunctionalities = findFolders(sInfo, biobj, dataStore);
      if(storeInFunctionalities.isEmpty()) {
        throw new Exception(" No functionality specified where store the new document");
      }
      // create biobject

      String jobName = jex.getJobDetail().getName();
      String completeLabel = "scheduler_" + jobName + "_" + docName;
      String label = "sched_" + String.valueOf(Math.abs(completeLabel.hashCode()));

      BIObject newbiobj = new BIObject();
      newbiobj.setDescription(docDesc);
      newbiobj.setCreationUser("scheduler");
      newbiobj.setLabel(label);
      newbiobj.setName(docName);
      newbiobj.setEncrypt(new Integer(0));
      newbiobj.setEngine(engine);
      newbiobj.setDataSourceId(biobj.getDataSourceId());
      newbiobj.setRelName("");
      newbiobj.setBiObjectTypeCode(officeDocDom.getValueCd());
      newbiobj.setBiObjectTypeID(officeDocDom.getValueId());
      newbiobj.setStateCode(relDom.getValueCd());
      newbiobj.setStateID(relDom.getValueId());
      newbiobj.setVisible(new Integer(1));
      newbiobj.setFunctionalities(storeInFunctionalities);
      IBIObjectDAO objectDAO = DAOFactory.getBIObjectDAO();
       Timestamp aoModRecDate;
      BIObject biobjexist = objectDAO.loadBIObjectByLabel(label);
      if(biobjexist==null){
        objectDAO.insertBIObject(newbiobj, objTemp);
      } else {
        newbiobj.setId(biobjexist.getId());
        objectDAO.modifyBIObject(newbiobj, objTemp);
      }
    } catch (Throwable t) {
      logger.error("Error while saving schedule result as new document", t );
    }finally{
      logger.debug("OUT");
    }
  }




  private void sendMail(SaveInfo sInfo, BIObject biobj,Map parMap, byte[] response, String retCT, String fileExt, IDataStore dataStore, String toBeAppendedToName, String toBeAppendedToDescription) {
    logger.debug("IN");
    try{

      String smtphost = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.smtphost");
        String smtpport = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.smtpport");
        int smptPort=25;
       
      if( (smtphost==null) || smtphost.trim().equals(""))
        throw new Exception("Smtp host not configured");
      if( (smtpport==null) || smtpport.trim().equals("")){
        throw new Exception("Smtp host not configured");
      }else{
        smptPort=Integer.parseInt(smtpport);
      }
       
       
      String from = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.from");
      if( (from==null) || from.trim().equals(""))
        from = "spagobi.scheduler@eng.it";
      String user = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.user");
      if( (user==null) || user.trim().equals(""))
        throw new Exception("Smtp user not configured");
      String pass = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.password");
      if( (pass==null) || pass.trim().equals(""))
        throw new Exception("Smtp password not configured");
     
      String mailSubj = sInfo.getMailSubj();
      mailSubj = StringUtilities.substituteParametersInString(mailSubj, parMap, null, false);

      String mailTxt = sInfo.getMailTxt();

      String[] recipients = findRecipients(sInfo, biobj, dataStore);
      if (recipients == null || recipients.length == 0) {
        logger.error("No recipients found for email sending!!!");
        return;
      }

      //Set the host smtp address
      Properties props = new Properties();
      props.put("mail.smtp.host", smtphost);
      props.put("mail.smtp.port", smptPort);
     
      props.put("mail.smtp.auth", "true");
      // create autheticator object
      Authenticator auth = new SMTPAuthenticator(user, pass);
      // open session
      Session session = Session.getDefaultInstance(props, auth);
      // create a message
      Message msg = new MimeMessage(session);
      // set the from and to address
      InternetAddress addressFrom = new InternetAddress(from);
      msg.setFrom(addressFrom);
      InternetAddress[] addressTo = new InternetAddress[recipients.length];
      for (int i = 0; i < recipients.length; i++)  {
        addressTo[i] = new InternetAddress(recipients[i]);
      }
      msg.setRecipients(Message.RecipientType.TO, addressTo);
      // Setting the Subject and Content Type
      String subject = mailSubj + " " + biobj.getName() + toBeAppendedToName;
      msg.setSubject(subject);
      // create and fill the first message part
      MimeBodyPart mbp1 = new MimeBodyPart();
      mbp1.setText(mailTxt + "\n" + toBeAppendedToDescription);
      // create the second message part
      MimeBodyPart mbp2 = new MimeBodyPart();
      // attach the file to the message

      SchedulerDataSource sds = new SchedulerDataSource(response, retCT, biobj.getName() + toBeAppendedToName + fileExt);
      mbp2.setDataHandler(new DataHandler(sds));
      mbp2.setFileName(sds.getName());
      // create the Multipart and add its parts to it
      Multipart mp = new MimeMultipart();
      mp.addBodyPart(mbp1);
      mp.addBodyPart(mbp2);
      // add the Multipart to the message
      msg.setContent(mp);
      // send message
      Transport.send(msg);
    } catch (Exception e) {
      logger.error("Error while sending schedule result mail",e);
    }finally{
      logger.debug("OUT");
    }
  }

  private String[] findRecipients(SaveInfo info, BIObject biobj,
      IDataStore dataStore) {
    logger.debug("IN");
    String[] toReturn = null;
    List<String> recipients = new ArrayList();
    try {
      recipients.addAll(findRecipientsFromFixedList(info));
    } catch (Exception e) {
      logger.error(e);
    }
    try {
      recipients.addAll(findRecipientsFromDataSet(info, biobj, dataStore));
    } catch (Exception e) {
      logger.error(e);
    }
    try {
      recipients.addAll(findRecipientsFromExpression(info, biobj));
    } catch (Exception e) {
      logger.error(e);
    }
    // validates addresses
    List<String> validRecipients = new ArrayList();
    Iterator it = recipients.iterator();
    while (it.hasNext()) {
      String recipient = (String) it.next();
      if (GenericValidator.isBlankOrNull(recipient) || !GenericValidator.isEmail(recipient)) {
        logger.error("[" + recipient + "] is not a valid email address.");
        continue;
      }
      if (validRecipients.contains(recipient))
        continue;
      validRecipients.add(recipient);
    }
    toReturn = validRecipients.toArray(new String[0]);
    logger.debug("OUT: returning " + toReturn);
    return toReturn;
  }

  private List<String> findRecipientsFromFixedList(SaveInfo info) throws Exception {
    logger.debug("IN");
    List<String> recipients = new ArrayList();
    if (info.isUseFixedRecipients()) {
      logger.debug("Trigger is configured to send mail to fixed recipients: " + info.getMailTos());
      if (info.getMailTos() == null || info.getMailTos().trim().equals("")) {
        throw new Exception("Missing fixed recipients list!!!");
      }
      // in this case recipients are fixed and separated by ","
      String[] fixedRecipients = info.getMailTos().split(",");
      logger.debug("Fixed recipients found: " + fixedRecipients);
      recipients.addAll(Arrays.asList(fixedRecipients));
    }
    logger.debug("OUT");
    return recipients;
  }

  private List<String> findRecipientsFromExpression(SaveInfo info, BIObject biobj) throws Exception {
    logger.debug("IN");
    List<String> recipients = new ArrayList();
    if (info.isUseExpression()) {
      logger.debug("Trigger is configured to send mail using an expression: " + info.getExpression());
      String expression = info.getExpression();
      if (expression == null || expression.trim().equals("")) {
        throw new Exception("Missing recipients expression!!!");
      }
      // building a map for parameters value substitution
      Map parametersMap = new HashMap();
      List parameters = biobj.getBiObjectParameters();
      Iterator it = parameters.iterator();
      while (it.hasNext()) {
        BIObjectParameter parameter = (BIObjectParameter) it.next();
        List values = parameter.getParameterValues();
        if (values != null && !values.isEmpty()) {
          parametersMap.put(parameter.getLabel(), values.get(0));
        } else {
          parametersMap.put(parameter.getLabel(), "");
        }
      }
      // we must substitute parameter values on the expression
      String recipientStr = StringUtilities.substituteParametersInString(expression, parametersMap,null, false);
      logger.debug("The expression, after substitution, now is [" + recipientStr + "].");
      String[] recipientsArray = recipientStr.split(",");
      logger.debug("Recipients found with expression: " + recipientsArray);
      recipients.addAll(Arrays.asList(recipientsArray));
    }
    logger.debug("OUT");
    return recipients;
  }

  private List<String> findRecipientsFromDataSet(SaveInfo info, BIObject biobj,
      IDataStore dataStore) throws Exception {
    logger.debug("IN");
    List<String> recipients = new ArrayList();
    if (info.isUseDataSet()) {
      logger.debug("Trigger is configured to send mail to recipients retrieved by a dataset");
      if (dataStore == null || dataStore.isEmpty()) {
        throw new Exception("The dataset in input is empty!! Cannot retrieve recipients from it.");
      }
      // in this case recipients must be retrieved by the dataset (which the datastore in input belongs to)
      // we must find the parameter value in order to filter the dataset
      String dsParameterLabel = info.getDataSetParameterLabel();
      logger.debug("The dataset will be filtered using the value of the parameter " + dsParameterLabel);
      // looking for the parameter
      List parameters = biobj.getBiObjectParameters();
      BIObjectParameter parameter = null;
      String codeValue = null;
      Iterator parameterIt = parameters.iterator();
      while (parameterIt.hasNext()) {
        BIObjectParameter aParameter = (BIObjectParameter) parameterIt.next();
        if (aParameter.getLabel().equalsIgnoreCase(dsParameterLabel)) {
          parameter = aParameter;
          break;
        }
      }
      if (parameter == null) {
        throw new Exception("The document parameter with label [" + dsParameterLabel + "] was not found. Cannot filter the dataset.");
      }

      // considering the first value of the parameter
      List values = parameter.getParameterValues();
      if (values == null || values.isEmpty()) {
        throw new Exception("The document parameter with label [" + dsParameterLabel + "] has no values. Cannot filter the dataset.");
      }

      codeValue = (String) values.get(0);
      logger.debug("Using value [" + codeValue + "] for dataset filtering...");

      Iterator it = dataStore.iterator();
      while (it.hasNext()) {
        String recipient = null;
        IRecord record = (IRecord)it.next();
        // the parameter value is used to filter on the first dataset field
        IField valueField = (IField) record.getFieldAt(0);
        Object valueObj = valueField.getValue();
        String value = null;
        if (valueObj != null)
          value = valueObj.toString();
        if (codeValue.equals(value)) {
          logger.debug("Found value [" + codeValue + "] on the first field of a record of the dataset.");
          // recipient address is on the second dataset field
          IField recipientField = (IField) record.getFieldAt(1);
          Object recipientFieldObj = recipientField.getValue();
          if (recipientFieldObj != null) {
            recipient = recipientFieldObj.toString();
            logger.debug("Found recipient [" + recipient + "] on the second field of the record.");
          } else {
            logger.warn("The second field of the record is null.");
          }
        }
        if (recipient != null) {
          recipients.add(recipient);
        }
      }
      logger.debug("Recipients found from dataset: " + recipients.toArray());
    }
    logger.debug("OUT");
    return recipients;
  }

  private List findFolders(SaveInfo info, BIObject biobj, IDataStore dataStore) {
    logger.debug("IN");
    List toReturn = null;
    List<String> folders = new ArrayList();
    try {
      folders.addAll(findFoldersFromFixedList(info));
    } catch (Exception e) {
      logger.error(e);
    }
    try {
      folders.addAll(findFoldersFromDataSet(info, biobj, dataStore));
    } catch (NullPointerException en) {
      logger.error("Folders defined into dataset " + info.getDataSetFolderLabel()+ "  not found.");
    } catch (Exception e) {
      logger.error(e);
    }
   
    toReturn = folders;
    logger.debug("OUT: returning " + toReturn);
    return toReturn;
  }
 
  private List findFoldersFromFixedList(SaveInfo info) throws Exception {
    logger.debug("IN");
    List folders = new ArrayList();
    String functIdsConcat = info.getFunctionalityIds();
    String[] functIds =  functIdsConcat.split(",");
    for(int i=0; i<functIds.length; i++) {
      String functIdStr = functIds[i];
      if(functIdStr.trim().equals(""))
        continue;
      Integer functId = Integer.valueOf(functIdStr);
      folders.add(functId);
    }
    logger.debug("OUT");
    return folders;
  }

  private List findFoldersFromDataSet(SaveInfo info, BIObject biobj,IDataStore dataStore) throws Exception {
    logger.debug("IN");
    List folders = new ArrayList();
    if (info.isUseFolderDataSet()) {
      logger.debug("Trigger is configured to save documents to folders retrieved by a dataset");
      if (dataStore == null || dataStore.isEmpty()) {
        throw new Exception("The dataset in input is empty!! Cannot retrieve folders from it.");
      }
      // in this case folders must be retrieved by the dataset (which the datastore in input belongs to)
      // we must find the parameter value in order to filter the dataset
      String dsParameterLabel = info.getDataSetFolderParameterLabel();
      logger.debug("The dataset will be filtered using the value of the parameter " + dsParameterLabel);
      // looking for the parameter
      List parameters = biobj.getBiObjectParameters();
      BIObjectParameter parameter = null;
      String codeValue = null;
      Iterator parameterIt = parameters.iterator();
      while (parameterIt.hasNext()) {
        BIObjectParameter aParameter = (BIObjectParameter) parameterIt.next();
        if (aParameter.getLabel().equalsIgnoreCase(dsParameterLabel)) {
          parameter = aParameter;
          break;
        }
      }
      if (parameter == null) {
        throw new Exception("The document parameter with label [" + dsParameterLabel + "] was not found. Cannot filter the dataset.");
      }

      // considering the first value of the parameter
      List values = parameter.getParameterValues();
      if (values == null || values.isEmpty()) {
        throw new Exception("The document parameter with label [" + dsParameterLabel + "] has no values. Cannot filter the dataset.");
      }

      codeValue = (String) values.get(0);
      logger.debug("Using value [" + codeValue + "] for dataset filtering...");
     
      Iterator it = dataStore.iterator();
      while (it.hasNext()) {
        String folder = null;
        IRecord record = (IRecord)it.next();
        // the parameter value is used to filter on the first dataset field
        IField valueField = (IField) record.getFieldAt(0);
        Object valueObj = valueField.getValue();
        String value = null;
        if (valueObj != null)
          value = valueObj.toString();
        if (codeValue.equals(value)) {
          logger.debug("Found value [" + codeValue + "] on the first field of a record of the dataset.");
          // recipient address is on the second dataset field
          IField folderField = (IField) record.getFieldAt(1);
          Object folderFieldObj = folderField.getValue();
          if (folderFieldObj != null) {
            folder = folderFieldObj.toString();
            logger.debug("Found folder [" + folder + "] on the second field of the record.");
          } else {
            logger.warn("The second field of the record is null.");
          }
        }
        if (folder != null) {
          //get the folder Id corresponding to the label folder and add it to the return list
          try{
            LowFunctionality func = DAOFactory.getLowFunctionalityDAO().loadLowFunctionalityByCode(folder, false);
            folders.add(func.getId());
          }catch (EMFUserError emf){
            logger.debug("Folder with code: " + folder + " not exists.");
          }
        }
      }
      logger.debug("Folders found from dataset: " + folders.toArray());
    }
    logger.debug("OUT");
    return folders;
  }
 
  private void sendToDl(SaveInfo sInfo, BIObject biobj, byte[] response, String retCT, String fileExt, String toBeAppendedToName, String toBeAppendedToDescription) {
    logger.debug("IN");
    try{

      String smtphost = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.smtphost");
      if( (smtphost==null) || smtphost.trim().equals(""))
        throw new Exception("Smtp host not configured");
      String from = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.from");
      if( (from==null) || from.trim().equals(""))
        from = "spagobi.scheduler@eng.it";
      String user = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.user");
      if( (user==null) || user.trim().equals(""))
        throw new Exception("Smtp user not configured");
      String pass = SingletonConfig.getInstance().getConfigValue("MAIL.PROFILES.scheduler.password");
      if( (pass==null) || pass.trim().equals(""))
        throw new Exception("Smtp password not configured");

      String mailTos = "";
      List dlIds = sInfo.getDlIds();
      Iterator it = dlIds.iterator();
      while(it.hasNext()){

        Integer dlId = (Integer)it.next();
        DistributionList dl = DAOFactory.getDistributionListDAO().loadDistributionListById(dlId);

        List emails = new ArrayList();
        emails = dl.getEmails();
        Iterator j = emails.iterator();
        while(j.hasNext()){
          Email e = (Email) j.next();
          String email = e.getEmail();
          String userTemp = e.getUserId();
          IEngUserProfile userProfile = GeneralUtilities.createNewUserProfile(userTemp);       
          if(ObjectsAccessVerifier.canSee(biobj, userProfile))  {       
            if (j.hasNext()) {mailTos = mailTos+email+",";}
            else {mailTos = mailTos+email;}
          }

        }
      }


      if( (mailTos==null) || mailTos.trim().equals("")) { 
        throw new Exception("No recipient address found");
      }

      String[] recipients = mailTos.split(",");
      //Set the host smtp address
      Properties props = new Properties();
      props.put("mail.smtp.host", smtphost);
      props.put("mail.smtp.auth", "true");
      // create autheticator object
      Authenticator auth = new SMTPAuthenticator(user, pass);
      // open session
      Session session = Session.getDefaultInstance(props, auth);
      // create a message
      Message msg = new MimeMessage(session);
      // set the from and to address
      InternetAddress addressFrom = new InternetAddress(from);
      msg.setFrom(addressFrom);
      InternetAddress[] addressTo = new InternetAddress[recipients.length];
      for (int i = 0; i < recipients.length; i++)  {
        addressTo[i] = new InternetAddress(recipients[i]);
      }
      msg.setRecipients(Message.RecipientType.TO, addressTo);
      // Setting the Subject and Content Type
      IMessageBuilder msgBuilder = MessageBuilderFactory.getMessageBuilder();
      String subject = biobj.getName() + toBeAppendedToName;
      msg.setSubject(subject);
      // create and fill the first message part
      //MimeBodyPart mbp1 = new MimeBodyPart();
      //mbp1.setText(mailTxt);
      // create the second message part
      MimeBodyPart mbp2 = new MimeBodyPart();
      // attach the file to the message
      SchedulerDataSource sds = new SchedulerDataSource(response, retCT, biobj.getName() + toBeAppendedToName + fileExt);
      mbp2.setDataHandler(new DataHandler(sds));
      mbp2.setFileName(sds.getName());
      // create the Multipart and add its parts to it
      Multipart mp = new MimeMultipart();
      //mp.addBodyPart(mbp1);
      mp.addBodyPart(mbp2);
      // add the Multipart to the message
      msg.setContent(mp);
      // send message
      Transport.send(msg);
    } catch (Exception e) {
      logger.error("Error while sending schedule result mail",e);
    }finally{
      logger.debug("OUT");
    }
  }


  private class SMTPAuthenticator extends javax.mail.Authenticator
  {
    private String username = "";
    private String password = "";

    public PasswordAuthentication getPasswordAuthentication()
    {
      return new PasswordAuthentication(username, password);
    }

    public SMTPAuthenticator(String user, String pass) {
      this.username = user;
      this.password = pass;
    }
  }


  private class SchedulerDataSource implements DataSource {

    byte[] content = null;
    String name = null;
    String contentType = null;

    public String getContentType() {
      return contentType;
    }

    public InputStream getInputStream() throws IOException {
      ByteArrayInputStream bais = new ByteArrayInputStream(content);
      return bais;
    }

    public String getName() {
      return name;
    }

    public OutputStream getOutputStream() throws IOException {
      return null;
    }

    public SchedulerDataSource(byte[] content, String contentType, String name) {
      this.content = content;
      this.contentType = contentType;
      this.name = name;
    }
  }

}
TOP

Related Classes of it.eng.spagobi.tools.scheduler.jobs.ExecuteBIDocumentJob$SchedulerDataSource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.