package com.casamind.adware.server.servlet.cron;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.UUID;
import java.util.logging.Logger;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.casamind.adware.server.dao.ObjectifyGenericDAO;
import com.casamind.adware.server.domain.Ad;
import com.casamind.adware.server.domain.Company;
import com.casamind.adware.server.domain.Product;
import com.casamind.adware.server.domain.Publisher;
import com.casamind.adware.server.domain.Resource;
import com.casamind.adware.server.domain.Schedule;
import com.casamind.adware.server.domain.Slot;
import com.casamind.adware.server.proxy.DatastoreProxy;
import com.casamind.adware.server.proxy.GDataDocumentsProxy;
import com.casamind.adware.server.servlet.mail.InboundMailHandler;
import com.casamind.adware.shared.GoogleCalendarTaskTypes;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
@SuppressWarnings("serial")
public class ScheduleBuilder extends HttpServlet {
private static final Logger log = Logger.getLogger(InboundMailHandler.class.getName());
private String gdataWebLogin, gdataWebPassword, documentsFeedUrl,
spreadsheetFeedUrl;
private GDataDocumentsProxy proxy;
private long gdataThreadSleep;
private long gdataConnectTimeout;
private int defaultSlotDurationInMinutes;
private String stringXML;
private Long scheduleId;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
ServletContext context = config.getServletContext();
defaultSlotDurationInMinutes = Integer.parseInt(context.getInitParameter("defaultSlotDurationInMinutes"));
gdataThreadSleep = Long.parseLong(context.getInitParameter("gdataThreadSleep"));
gdataConnectTimeout = Long.parseLong(context.getInitParameter("gdataConnectTimeout"));
gdataWebLogin = context.getInitParameter("gdataWebLogin");
gdataWebPassword = context.getInitParameter("gdataWebPassword");
documentsFeedUrl = context.getInitParameter("documentsFeedUrl");
spreadsheetFeedUrl = context.getInitParameter("spreadsheetFeedUrl");
proxy = new GDataDocumentsProxy(documentsFeedUrl, spreadsheetFeedUrl, gdataWebLogin, gdataWebPassword, gdataThreadSleep, gdataConnectTimeout);
stringXML = "";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.info("Entering ScheduleBuilder Servlet...");
Calendar calendar = Calendar.getInstance();
SimpleDateFormat logFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm Z");
calendar.setTimeZone(TimeZone.getTimeZone("Africa/Casablanca"));
// get a calendar instance, which defaults to "now"
// get a date to represent "today" midnight
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
Date startDate, endDate;
startDate = calendar.getTime();
// add one day to the date/calendar
calendar.add(Calendar.MONTH, 1);
calendar.add(Calendar.DAY_OF_YEAR, 1);
// now get "tomorrow" midnight
endDate = calendar.getTime();
log.info("Working on dates between '" + logFormatter.format(startDate) + "' and '" + logFormatter.format(endDate) + "'");
log.info("Purging old ads...");
purgeOldAds(startDate, endDate);
log.info("Building schedule...");
getScheduleInXml(calendar, startDate, endDate);
log.info("Uploading to Google Docs...");
uploadToGoogleDocs();
log.info("Queueing Google Calendar Task...");
queueGDataCalendarTask();
log.info("Exiting ScheduleBuilder Servlet...");
response.getWriter().println("Finished at: " + new Date());
}
private void queueGDataCalendarTask() {
if (scheduleId != null) {
QueueFactory.getQueue("gdata").add(TaskOptions.Builder.withUrl("/tasks/gdata/calendar").param("task", GoogleCalendarTaskTypes.SCHEDULE).param("entityId", scheduleId.toString()));
log.info("Google Calendar task queued successfully.");
} else {
log.warning("scheduleId == null\nNothing to do on Google Calendar.");
}
}
private void purgeOldAds(Date startDate, Date endDate) {
SimpleDateFormat logFormatter = new SimpleDateFormat("dd MMM yyyy HH:mm Z");
ObjectifyGenericDAO<Ad> dao = new ObjectifyGenericDAO<Ad>(Ad.class);
List<Ad> ads = (List<Ad>) dao.ofy().query(Ad.class).filter("startTime >=", startDate).filter("startTime <", endDate).list();
if (ads.size() > 0) {
log.info("Found " + ads.size() + " ads!");
dao.deleteAll(ads);
ads.clear();
ads.addAll((List<Ad>) dao.ofy().query(Ad.class).filter("startTime >=", startDate).filter("startTime <", endDate).list());
if (ads.size() == 0) {
log.info("Deleted all ads from datastore.");
} else {
String list = "\n\t";
for (Ad ad : ads) {
list += "\n\tid: " + ad.getId() + "\tstart: " + logFormatter.format(startDate) + "\tend: " + logFormatter.format(endDate);
}
log.warning("The following ads were not deleted." + list);
}
} else {
log.info("No ads found. Nothing to purge.");
}
}
private String uploadToGoogleDocs() {
if (stringXML != null && !"".equals(stringXML)) {
DocumentListEntry entry = null;
try {
log.info("Looking for 'ads' file on Google Docs account: " + gdataWebLogin);
entry = proxy.findDocumentEntry("ads");
if (entry == null) {
log.warning("Cound not find 'ads' file on Google Docs account!");
entry = proxy.createDocumentFile("document", "ads", "schedule");
log.info("Created new entry on " + entry.getDocumentLink().getHref());
log.info("Sleeping " + gdataThreadSleep + " milliseconds");
Thread.sleep(gdataThreadSleep);
log.info("Wake up!");
} else {
log.info("Found 'ads' file on Google Docs account!");
}
log.info("Updating document content with XML data...");
entry = proxy.updateDocumentFile(entry, stringXML, "text/plain");
log.info("Success! Updated entry: " + entry.getDocumentLink().getHref());
} catch (AuthenticationException e) {
return e.getMessage();
} catch (MalformedURLException e) {
return e.getMessage();
} catch (IOException e) {
return e.getMessage();
} catch (ServiceException e) {
return e.getMessage();
} catch (InterruptedException e) {
e.printStackTrace();
}
return entry.getDocumentLink().getHref();
} else {
return "No slots were found! See server logs for more details.";
}
}
public void getScheduleInXml(Calendar calendar, Date startDate, Date endDate) {
try {
log.info("Building XML file.");
ObjectifyGenericDAO<Product> productDAO = new ObjectifyGenericDAO<Product>(Product.class);
ObjectifyGenericDAO<Schedule> scheduleDAO = new ObjectifyGenericDAO<Schedule>(Schedule.class);
ObjectifyGenericDAO<Ad> adDAO = new ObjectifyGenericDAO<Ad>(Ad.class);
StringBuilder sb = new StringBuilder();
SimpleDateFormat xmlFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'+01:00'");
SimpleDateFormat logFormatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm Z");
Date startTime, endTime;
log.info("Querying slots...");
List<Slot> slots = (List<Slot>) new ObjectifyGenericDAO<Slot>(Slot.class).ofy().query(Slot.class).filter("startDate >=", startDate).filter("startDate <", endDate).list();
if (slots.size() > 0) {
log.info("Found " + slots.size() + " slots!");
// sb.append("<!-- ");
// sb.append("Automatically generated by cron job on " + new
// Date());
// sb.append(" -->");
// sb.append("\n");
sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
sb.append("\n");
sb.append("<ScheduleDTO xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
sb.append(" ");
sb.append("xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">");
Schedule schedule = new Schedule(new Date());
schedule.setMinDate(startDate);
schedule.setMaxDate(endDate);
scheduleDAO.put(schedule);
sb.append("\n<Id>" + UUID.randomUUID().toString() + "</Id>");
sb.append("\n<Date>" + xmlFormatter.format(schedule.getDateCreated()) + "</Date>");
sb.append("\n<Ads>");
for (Slot slot : slots) {
startTime = xmlFormatter.parse(xmlFormatter.format(slot.getStartDate()));
endTime = xmlFormatter.parse(xmlFormatter.format(slot.getStartDate()));
while (endTime.compareTo(slot.getEndDate()) < 0) {
if (slot.getProductIds().size() == 0) {
endTime = xmlFormatter.parse(xmlFormatter.format(slot.getEndDate()));
log.warning("Slot from '" + logFormatter.format(slot.getStartDate()) + "' to '" + logFormatter.format(slot.getEndDate()) + "' did not have any product! The slot will be skipped.");
} else {
for (int index = 0 ; index < slot.getProductIds().size() && endTime.compareTo(slot.getEndDate()) < 0; index++) {
Long prId = slot.getProductIds().get(index);
Product product = DatastoreProxy.getProductById(prId);
if (product != null) {
calendar.setTime(startTime);
calendar.add(Calendar.MINUTE, defaultSlotDurationInMinutes);
endTime = calendar.getTime();
List<Resource> resources = DatastoreProxy.getResourcesByProductId(product.getId());
List<String> resourcesURLs = new ArrayList<String>();
if (resources != null) {
for (Resource r : resources) {
if (r != null) {
resourcesURLs.add(r.getImageURL());
}
}
}
Long pbId = product.getPublisherId();
if (pbId != null) {
Publisher publisher = DatastoreProxy.getPublisherById(pbId);
if (publisher != null) {
Long cpId = publisher.getCompanyId();
if (cpId != null) {
Company company = DatastoreProxy.getCompanyById(cpId);
if (company != null) {
Ad ad = new Ad(startTime, endTime, company.getLastname(), product.getName(), product.getSlogan(), product.getText(), product.getLink(), resourcesURLs, true, new Double(1));
adDAO.put(ad);
schedule.getAdIds().add(ad.getId());
startTime = calendar.getTime();
sb.append("\n<AdDTO>");
sb.append("\n<Id>" + ad.getUUID() + "</Id>");
sb.append("\n<Company>");
sb.append("\n<Id>" + company.getUUID() + "</Id>");
sb.append("\n<Name>" + ad.getCompany() + "</Name>");
sb.append("\n</Company>");
sb.append("\n<Publisher>");
sb.append("\n<Id>" + publisher.getUUID() + "</Id>");
sb.append("\n<Name>" + publisher.getFirstname() + " " + publisher.getLastname() + "</Name>");
sb.append("\n</Publisher>");
sb.append("\n<Product>");
sb.append("\n<Id>" + product.getUUID() + "</Id>");
sb.append("\n<Name>" + ad.getProduct() + "</Name>");
sb.append("\n<Text>" + ad.getText() + "</Text>");
sb.append("\n<Link>" + ad.getLink() + "</Link>");
sb.append("\n<Slogan>" + ad.getSlogan() + "</Slogan>");
sb.append("\n</Product>");
sb.append("\n<Start>" + xmlFormatter.format(ad.getStartTime()) + "</Start>");
sb.append("\n<End>" + xmlFormatter.format(ad.getEndTime()) + "</End>");
sb.append("\n<IsCommercial>" + ad.isCommercial() + "</IsCommercial>");
sb.append("\n<Weight>" + ad.getWeight() + "</Weight>");
sb.append("\n<Resources>");
for (Resource rs : resources) {
sb.append("\n<ResourceDTO>");
sb.append("\n<Filename>" + rs.getUUID() + "</Filename>");
sb.append("\n<ImageURL>" + rs.getImageURL() + "</ImageURL>");
sb.append("\n</ResourceDTO>");
}
sb.append("\n</Resources>");
sb.append("\n</AdDTO>");
log.info("Appended: " + ad);;
}
}
}
}
productDAO.put(product);
}
}
}
}
}
sb.append("\n</Ads>");
sb.append("\n</ScheduleDTO>");
Schedule entity = DatastoreProxy.createSchedule(schedule);
this.scheduleId = entity == null ? null : entity.getId();
this.stringXML = sb.toString();
} else {
log.warning("No slots were found!");
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}