/**
* Copyright (C) 2009 fgrilli <federico.grilli@gmail.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, either version 3 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.google.code.mgnlgroovy.scheduler.admin;
import info.magnolia.cms.beans.config.ContentRepository;
import info.magnolia.cms.beans.runtime.Document;
import info.magnolia.cms.core.Content;
import info.magnolia.cms.core.search.Query;
import info.magnolia.cms.core.search.QueryManager;
import info.magnolia.cms.core.search.QueryResult;
import info.magnolia.cms.util.AlertUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.module.admininterface.ValidatableMVCHandler;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.RepositoryException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.chain.CatalogFactory;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.code.mgnlgroovy.scheduler.JobDefinition;
import com.google.code.mgnlgroovy.scheduler.manager.JobDefinitionManager;
/**
* @author fgrilli
* @version $Id: SchedulerEditJob.java 3 2009-04-16 22:48:19Z federico.grilli $
*/
public class SchedulerEditJob extends ValidatableMVCHandler
{
protected static final Logger log = LoggerFactory.getLogger(SchedulerEditJob.class);
private static final String LINE_SEP = System.getProperty("line.separator");
private JobDefinition job = new JobDefinition();
private JobDefinitionManager jobDefinitionManager = JobDefinitionManager.Factory.getInstance();
private boolean newJobDefinition = false;
private boolean editJobDefinition = false;
private boolean deleteJobDefinition = false;
private String UUID;
private Document mgnlFileImport;
private Map<String, Set<String>> commands = findCommandNames();
private String jobType;
public SchedulerEditJob(String name, HttpServletRequest request, HttpServletResponse response)
{
super(name, request, response);
}
public String newJob()
{
log.info("creating new job");
return this.show();
}
public String editJob()
{
try
{
job = jobDefinitionManager.getJobDefinitionByUUID(UUID);
log.info("editing job {}", job.getName());
}
catch (ItemNotFoundException e)
{
log.error(e.getMessage());
}
catch (RepositoryException e)
{
log.error(e.getMessage());
}
return this.show();
}
public String deleteJob()
{
try
{
editJobDefinition = false;
newJobDefinition = false;
deleteJobDefinition = true;
job = jobDefinitionManager.getJobDefinitionByUUID(UUID);
log.info("deleting job {}", job.getName());
jobDefinitionManager.removeJobDefinition(UUID);
AlertUtil.setMessage("Job <strong>" + job.getName() + "</strong> deleted successfully");
}
catch (ItemNotFoundException e)
{
log.error(e.getMessage());
AlertUtil.setException(e.getMessage(), e);
}
catch (RepositoryException e)
{
log.error(e.getMessage());
AlertUtil.setException(e.getMessage(), e);
}
return this.show();
}
public String updateJob()
{
try
{
jobDefinitionManager.saveOrUpdateJobDefinition(job);
AlertUtil.setMessage("Job updated successfully");
}
catch (ItemExistsException e)
{
log.error(e.getMessage());
AlertUtil.setException(e.getMessage(), e);
}
catch (RepositoryException e)
{
log.error(e.getMessage());
AlertUtil.setException(e.getMessage(), e);
}
return this.show();
}
public String createJob()
{
try
{
if (StringUtils.isNotBlank(job.getGroovyScript()))
{
job.setCatalog(null);
job.setCommand(null);
}
UUID = jobDefinitionManager.saveOrUpdateJobDefinition(job);
job = jobDefinitionManager.getJobDefinitionByUUID(UUID);
AlertUtil.setMessage("Job created successfully");
newJobDefinition = false;
editJobDefinition = true;
setCommand("editJob");
}
catch (ItemExistsException e)
{
log.error(e.getMessage());
AlertUtil.setException(e.getMessage(), e);
}
catch (RepositoryException e)
{
log.error(e.getMessage());
AlertUtil.setException(e.getMessage(), e);
}
return this.show();
}
public String getCatalog()
{
return job.getCatalog();
}
public String getCommandName()
{
return job.getCommand();
}
public String getCron()
{
return job.getCron();
}
public Long getEndTime()
{
return job.getEndTime();
}
public String getGroovyScript()
{
return job.getGroovyScript();
}
public String getJobName()
{
return job.getName();
}
public Map getJobParams()
{
log.debug("getJobParams {}", job.getParams());
return job.getParams();
}
public String getJobParamsAsString()
{
return ArrayUtils.toString(job.getParams());
}
public Long getStartTime()
{
return job.getStartTime();
}
public boolean isActive()
{
return job.isActive();
}
public boolean isTerminatedWithError()
{
return job.isTerminatedWithError();
}
public void setActive(boolean active)
{
job.setActive(active);
}
public void setCatalog(String catalogue)
{
job.setCatalog(catalogue);
}
public void setCommandName(String command)
{
job.setCommand(command);
}
public void setCron(String cron)
{
job.setCron(cron);
}
public void setEndTime(Long endTime)
{
job.setEndTime(endTime);
}
public void setGroovyScript(String groovyScript)
{
job.setGroovyScript(groovyScript);
}
public void setJobName(String name)
{
job.setName(name);
}
public void setJobParams(String params)
{
if (StringUtils.isEmpty(params))
return;
String[] keyValuePairs = params.split(";");
Map<String, String> map = new LinkedHashMap<String, String>();
for (String keyValue : keyValuePairs)
{
String[] pair = keyValue.split(":");
map.put(pair[0], pair[1]);
}
job.setParams(map);
}
public void setStartTime(Long startTime)
{
job.setStartTime(startTime);
}
public void setTerminatedWithError(boolean terminatedWithError)
{
job.setTerminatedWithError(terminatedWithError);
}
public String getUUID()
{
return UUID;
}
public void setUUID(String uuid)
{
UUID = uuid;
}
public boolean isNewJobDefinition()
{
return newJobDefinition;
}
public void setNewJobDefinition(boolean newJobDefinition)
{
this.newJobDefinition = newJobDefinition;
}
public boolean isEditJobDefinition()
{
return editJobDefinition;
}
public void setEditJobDefinition(boolean editJobDefinition)
{
this.editJobDefinition = editJobDefinition;
}
public boolean isDeleteJobDefinition()
{
return deleteJobDefinition;
}
public void setDeleteJobDefinition(boolean deleteJobDefinition)
{
this.deleteJobDefinition = deleteJobDefinition;
}
public String getDescription()
{
return job.getDescription();
}
public void setDescription(String description)
{
job.setDescription(description);
}
public Document getMgnlFileImport()
{
return mgnlFileImport;
}
public void setMgnlFileImport(Document mgnlFileImport)
{
this.mgnlFileImport = mgnlFileImport;
}
public Long getNextFireTime()
{
return job.getNextFireTime();
}
public void setNextFireTime(Long nextFireTime)
{
job.setNextFireTime(nextFireTime);
}
public void setJobType(String jobType)
{
this.jobType = jobType;
}
public String getJobType()
{
return jobType;
}
public String loadGroovyScript()
{
if (mgnlFileImport == null)
{
String msg = "Please, select a file";
log.warn(msg);
AlertUtil.setException(msg, new Exception());
return this.show();
}
if (!mgnlFileImport.getExtension().equalsIgnoreCase("groovy"))
{
String msg = mgnlFileImport.getFileNameWithExtension() + " doesn't seem to be a valid groovy file";
log.warn(msg);
AlertUtil.setException(msg, new Exception());
return this.show();
}
job.setGroovyScript(readFile(mgnlFileImport.getFile()));
String msg = mgnlFileImport.getFileNameWithExtension() + " loaded";
log.info(msg);
AlertUtil.setMessage(msg);
return this.show();
}
private String readFile(File file)
{
StringBuilder contents = new StringBuilder();
try
{
// use buffering, reading one line at a time
// FileReader always assumes default encoding is OK!
BufferedReader input = new BufferedReader(new FileReader(file));
try
{
String line = null;
/*
* readLine is a bit quirky : it returns the content of a line MINUS the newline. it returns null only
* for the END of the stream. it returns an empty String if two newlines appear in a row.
*/
while ((line = input.readLine()) != null)
{
contents.append(line);
contents.append(LINE_SEP);
}
}
finally
{
IOUtils.closeQuietly(input);
}
}
catch (IOException ex)
{
log.error(ex.getMessage());
}
return contents.toString();
}
@SuppressWarnings("unchecked")
private Map<String, Set<String>> findCommandNames()
{
QueryManager qm = MgnlContext.getQueryManager(ContentRepository.CONFIG);
Iterator catalogIter = CatalogFactory.getInstance().getNames();
Map<String, Set<String>> retVal = new HashMap<String, Set<String>>();
while (catalogIter.hasNext())
{
String catalog = (String) catalogIter.next();
try
{
Query q = qm.createQuery("//commands//" + catalog + "//*", Query.XPATH);
QueryResult qr = q.execute();
Collection collection = qr.getContent("mgnl:contentNode");
if (CollectionUtils.isEmpty(collection))
continue;
Iterator commandIter = collection.iterator();
Set<String> commands = new HashSet<String>();
while (commandIter.hasNext())
{
Content content = (Content) commandIter.next();
commands.add(content.getName());
}
retVal.put(catalog, commands);
}
catch (RepositoryException e)
{
log.warn(e.getMessage());
throw new RuntimeException(e);
}
}
return retVal;
}
public void setCommands(Map<String, Set<String>> commands)
{
this.commands = commands;
}
public Map<String, Set<String>> getCommands()
{
return commands;
}
@Override
public boolean validate(String command, List<Exception> errors)
{
if (command.equals("newJob") || command.equals("editJob") || command.equals("deleteJob"))
return true;
// TODO this is a workaround, as long as I will understand
// how to populate automatically a Map from an http request parameter with BeanUtils
setJobParams(getRequest().getParameter("jobParams"));
log.debug("command is {}", command);
if (!checkMandatoryFields(errors))
return false;
if ("updateJob".equals(command))
{
log.debug("validating update");
if (getCommandName() == null && StringUtils.isBlank(getGroovyScript()))
{
String msg = "groovy script cannot be blank";
log.warn(msg);
errors.add(new Exception(msg));
return false;
}
}
else if ("createJob".equals(command))
{
log.debug("validating new job");
if ("groovyJob".equals(getJobType()) && StringUtils.isBlank(getGroovyScript()))
{
String msg = "groovy script cannot be blank";
log.warn(msg);
errors.add(new Exception(msg));
return false;
}
if (jobDefinitionManager.sameJobNameExists(getJobName()))
{
String msg = "job <strong>" + getJobName() + "</strong> already exists. Please choose another name.";
log.warn(msg);
errors.add(new Exception(msg));
return false;
}
}
if (getEndTime() != 0 && (getStartTime() > getEndTime()))
{
String msg = "Start time cannot be after end time";
log.warn(msg);
errors.add(new Exception(msg));
return false;
}
return true;
}
private boolean checkMandatoryFields(List<Exception> errors)
{
if (StringUtils.isBlank(getJobName()))
{
String msg = "job name cannot be blank";
log.warn(msg);
errors.add(new Exception(msg));
}
if (StringUtils.isBlank(getCron()))
{
String msg = "cron expression cannot be blank";
log.warn(msg);
errors.add(new Exception(msg));
}
if ("groovyJob".equals(getJobType()) && StringUtils.isBlank(getGroovyScript()))
{
String msg = "groovy script cannot be blank";
log.warn(msg);
errors.add(new Exception(msg));
}
if (isActive() && getStartTime() == 0)
{
String msg = "Please, choose a start time or set your job as inactive.";
log.warn(msg);
errors.add(new Exception(msg));
}
return errors.isEmpty();
}
}