/*
* Copyright 2010 Research Studios Austria Forschungsgesellschaft mBH
*
* This file is part of easyrec.
*
* easyrec 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.
*
* easyrec 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 easyrec. If not, see <http://www.gnu.org/licenses/>.
*/
package org.easyrec.servlet;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.easyrec.model.core.web.ErrorMessage;
import org.easyrec.model.core.web.Message;
import org.easyrec.model.core.web.RemoteTenant;
import org.easyrec.model.core.web.SuccessMessage;
import org.easyrec.model.plugin.LogEntry;
import org.easyrec.model.plugin.NamedConfiguration;
import org.easyrec.model.plugin.archive.ArchivePseudoConfiguration;
import org.easyrec.model.plugin.archive.ArchivePseudoGenerator;
import org.easyrec.model.plugin.archive.ArchivePseudoStatistics;
import org.easyrec.model.web.*;
import org.easyrec.plugin.configuration.GeneratorContainer;
import org.easyrec.plugin.container.PluginRegistry;
import org.easyrec.plugin.model.PluginId;
import org.easyrec.plugin.stats.GeneratorStatistics;
import org.easyrec.plugin.stats.StatisticsConstants;
import org.easyrec.service.core.TenantService;
import org.easyrec.service.web.RemoteTenantService;
import org.easyrec.service.web.nodomain.ShopRecommenderService;
import org.easyrec.store.dao.web.RemoteTenantDAO;
import org.easyrec.util.core.Security;
import org.easyrec.vocabulary.MSG;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* Servlet implementation class for Servlet: PluginStarter
*/
public class PluginStarter extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
@SuppressWarnings({"UnusedDeclaration"})
static final long serialVersionUID = 1L;
private TenantService tenantService;
private RemoteTenantDAO remoteTenantDAO;
private RemoteTenantService remoteTenantService;
private ShopRecommenderService shopRecommenderService;
private EasyRecSettings easyrecSettings;
private PluginRegistry pluginRegistry;
private GeneratorContainer generatorContainer;
private boolean initialized = false;
// logging
private final Log logger = LogFactory.getLog(this.getClass());
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#HttpServlet()
*/
public PluginStarter() {
super();
}
private void initialize() {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
this.tenantService = context.getBean("tenantService", TenantService.class);
this.remoteTenantDAO = context.getBean("remoteTenantDAO", RemoteTenantDAO.class);
this.remoteTenantService = context.getBean("remoteTenantService", RemoteTenantService.class);
this.shopRecommenderService = context.getBean("shopRecommenderService", ShopRecommenderService.class);
this.easyrecSettings = context.getBean("easyrecSettings", EasyRecSettings.class);
this.pluginRegistry = context.getBean("pluginRegistry", PluginRegistry.class);
this.generatorContainer = context.getBean("generatorContainer", GeneratorContainer.class);
initialized = true;
}
@SuppressWarnings({"UnusedDeclaration"})
@XmlRootElement(name = "generators")
private static class GeneratorsResponse {
@XmlElement(name = "generator")
private List<GeneratorResponse> generatorResponses;
private GeneratorsResponse() {}
public GeneratorsResponse(List<GeneratorResponse> generatorResponses) {
this.generatorResponses = generatorResponses;
}
public List<GeneratorResponse> getGeneratorResponses() {
return generatorResponses;
}
}
@SuppressWarnings({"UnusedDeclaration"})
@XmlRootElement(name = "generator")
private static class GeneratorResponse {
@XmlElement(name = "success", required = false)
private SuccessMessage successMessage;
@XmlElement(name = "error", required = false)
private ErrorMessage errorMessage;
@XmlElement(name = "action")
private String action;
@XmlAttribute(name = "id")
@XmlJavaTypeAdapter(PluginId.URIAdapter.class)
private PluginId pluginId;
private GeneratorResponse() {}
public GeneratorResponse(Message message, String action, PluginId pluginId) {
if (message.getClass().equals(ErrorMessage.class))
errorMessage = (ErrorMessage) message;
else
successMessage = (SuccessMessage) message;
this.action = action;
this.pluginId = pluginId;
}
public Message getMessage() {
return errorMessage != null ? errorMessage : successMessage;
}
public String getAction() {
return action;
}
public PluginId getPluginId() {
return pluginId;
}
}
private void marshalResponseXml(GeneratorsResponse generatorsResponse, OutputStream output) {
try {
Set<Class<?>> classes = Sets.newHashSet(GeneratorsResponse.class, GeneratorResponse.class,
ErrorMessage.class, SuccessMessage.class);
for(GeneratorResponse generatorResponse : generatorsResponse.getGeneratorResponses()) {
Object content = generatorResponse.getMessage().getContent();
// TODO workaround classes that are not @XmlRootElement annotated
if(content != null)
classes.add(content.getClass());
}
Class[] classesArray = classes.toArray(new Class[classes.size()]);
JAXBContext jaxbContext = JAXBContext.newInstance(classesArray);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(generatorsResponse, output);
} catch (JAXBException e) {
logger.error("failed to marshal response!", e);
}
}
private void marshalResponseXml(GeneratorResponse generatorResponse, OutputStream output) {
try {
Set<Class<?>> classes = Sets.newHashSet(GeneratorsResponse.class, GeneratorResponse.class,
ErrorMessage.class, SuccessMessage.class);
Object content = generatorResponse.getMessage().getContent();
// TODO workaround classes that are not @XmlRootElement annotated
if(content != null)
classes.add(content.getClass());
Class[] classesArray = classes.toArray(new Class[classes.size()]);
JAXBContext jaxbContext = JAXBContext.newInstance(classesArray);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(generatorResponse, output);
} catch (JAXBException e) {
logger.error("failed to marshal response!", e);
}
}
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/xml; charset=utf-8");
if (!initialized) {
initialize();
}
if (!easyrecSettings.isGenerator()) {
marshalResponseXml(new GeneratorResponse(MSG.GENERATOR_ALREADY_EXECUTING.replace(
"Cannot start the generator! easyrec is running in frontend mode!"), "startPlugin",
new PluginId("http://www.easyrec.org/internal/starter", easyrecSettings.getVersion())),
response.getOutputStream());
return;
}
if (!pluginRegistry.isAllExecutablesStopped()) { // already running
marshalResponseXml(new GeneratorResponse(MSG.GENERATOR_ALREADY_EXECUTING
.replace("Cannot start the calculation! Another calculation is currently running!"), "startPlugin",
new PluginId("http://www.easyrec.org/internal/starter", easyrecSettings.getVersion())),
response.getOutputStream());
return;
}
String tenantId = request.getParameter("tenantId");
String operatorId = request.getParameter("operatorId");
RemoteTenant remoteTenant = remoteTenantDAO.get(operatorId, tenantId);
if (remoteTenant == null || !Security.isSignedIn(request)) {
logger.info("No tenant specified. Start mining for all tenants.");
marshalResponseXml(new GeneratorResponse(MSG.GENERATOR_ALREADY_EXECUTING
.replace("No tenant specified!"), "startPlugin",
new PluginId("http://www.easyrec.org/internal/starter", easyrecSettings.getVersion())),
response.getOutputStream());
return;
}
logger.info("Starting generator for tenant: " + operatorId + ":" + tenantId);
final Properties tenantConfig = tenantService.getTenantConfig(remoteTenant.getId());
if (tenantConfig == null) {
logger.warn("could not get tenant configuration, aborting");
marshalResponseXml(new GeneratorResponse(MSG.GENERATOR_ALREADY_EXECUTING
.replace("Could not get tenant configuration!"), "startPlugin",
new PluginId("http://www.easyrec.org/internal/starter", easyrecSettings.getVersion())),
response.getOutputStream());
return;
}
if ("true".equals(tenantConfig.getProperty(RemoteTenant.AUTO_ARCHIVER_ENABLED))) {
String daysString = tenantConfig.getProperty(RemoteTenant.AUTO_ARCHIVER_TIME_RANGE);
final int days = Integer.parseInt(daysString);
ArchivePseudoConfiguration configuration = new ArchivePseudoConfiguration(days);
configuration.setAssociationType("ARCHIVE");
NamedConfiguration namedConfiguration = new NamedConfiguration(remoteTenant.getId(), 0,
ArchivePseudoGenerator.ID, "Archive", configuration, true);
logger.info("Archiving actions older than " + days + " day(s)");
generatorContainer.runGenerator(namedConfiguration,
// create a log entry only for archiver runs where actions were actually archived
// --> remove log entries where the number of archived actions is 0
new Predicate<GeneratorStatistics>() {
public boolean apply(GeneratorStatistics input) {
ArchivePseudoStatistics archivePseudoStatistics = (ArchivePseudoStatistics) input;
return archivePseudoStatistics.getNumberOfArchivedActions() > 0;
}
}, true);
}
List<LogEntry> generatorRuns = generatorContainer.runGeneratorsForTenant(remoteTenant.getId());
List<GeneratorResponse> responses = Lists.transform(generatorRuns, new Function<LogEntry, GeneratorResponse>() {
public GeneratorResponse apply(LogEntry input) {
Message message =
input.getStatistics().getClass().equals(StatisticsConstants.ExecutionFailedStatistics.class)
? MSG.GENERATOR_FINISHED_FAIL : MSG.GENERATOR_FINISHED_SUCCESS;
message = message.content(input.getStatistics());
return new GeneratorResponse(message, "startPlugin", input.getPluginId());
}
});
GeneratorsResponse generatorsResponse = new GeneratorsResponse(responses);
// TODO send call to shoprecommendersystem to cache most viewed item of all time
/*
// send a call to MOST VIEWED ITEM of ALL TIME to be cached
shopRecommenderService.mostViewedItems(r.getId(), Item.DEFAULT_STRING_ITEM_TYPE, 50, "ALL", null,
new Session(null, request.getRemoteAddr()));
*/
remoteTenantService.updateTenantStatistics(remoteTenant.getId());
marshalResponseXml(generatorsResponse, response.getOutputStream());
}
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}