/* * Copyright 2010 Fred Sauer * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.allen_sauer.gwt.voices.crowd.server; import com.allen_sauer.gwt.voices.crowd.shared.TestResultSummary; import com.allen_sauer.gwt.voices.crowd.shared.TestResults; import com.allen_sauer.gwt.voices.crowd.shared.UserAgent; import com.allen_sauer.gwt.voices.crowd.shared.UserAgentSummary; import com.google.appengine.api.mail.MailService; import com.google.appengine.api.mail.MailService.Message; import com.google.appengine.api.mail.MailServiceFactory; import com.google.appengine.api.memcache.MemcacheService; import com.google.appengine.api.memcache.MemcacheServiceFactory; import com.googlecode.objectify.Objectify; import com.googlecode.objectify.ObjectifyService; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Util { private static final Logger logger = Logger.getLogger(Util.class.getName()); private static final int BUFFER_SIZE = 4096; public static TestResultSummary incrementTestResultCount(UserAgent userAgent, String gwtUserAgent, TestResults testResults) throws IOException { logger.log(Level.INFO, "incrementTestResultCount(pm, " + userAgent + ", " + gwtUserAgent + ", " + testResults + ")"); UserAgentSummary userAgentSummary = lookupPrettyUserAgent(userAgent.toString(), gwtUserAgent); logger.log(Level.INFO, "userAgentSummary=" + userAgentSummary); Objectify ofy = ObjectifyService.begin(); com.googlecode.objectify.Query<TestResultSummary> q = ofy.query(TestResultSummary.class); q.filter("userAgent", userAgent.toString()); q.filter("gwtUserAgent", gwtUserAgent); q.filter("results", testResults.toString()); List<TestResultSummary> summaryList = q.list(); logger.log(Level.INFO, "summaryList.size()=" + summaryList.size()); TestResultSummary summary; if (summaryList.isEmpty()) { summary = new TestResultSummary(userAgent, userAgentSummary.getPrettyUserAgent(), gwtUserAgent, testResults); String subject = "new user-agent"; String msg = "TestResultSummary=" + summary; sendEmail(subject, msg); } else { summary = summaryList.get(0); if (summaryList.size() > 1) { // merge rows in case race condition caused > 1 row to be inserted TestResultSummary anotherSummary = summaryList.get(1); summary.incrementCount(anotherSummary.getCount()); ofy.delete(anotherSummary); } // count the current test results summary.incrementCount(1); if (summary.getPrettyUserAgent() == null) { summary.setPrettyUserAgent(lookupPrettyUserAgent(userAgent.toString(), gwtUserAgent).getPrettyUserAgent()); } } ofy.put(summary); return summary; } public static void sendEmail(String subject, String messageText) { try { MailService ms = MailServiceFactory.getMailService(); Message message = new Message(); message.setSender("fredsa@google.com"); message.setSubject("{gwt-voices} " + subject); message.setTextBody(messageText); ms.sendToAdmins(message); } catch (Exception e) { logger.log(Level.SEVERE, "failed to send email", e); } } /** * Lookup a human readable user agent. * @param userAgentString raw user agent string * @param gwtUserAgent GWT {code user.agent} property value * * @return human readable user agent * @throws IOException maybe */ public static UserAgentSummary lookupPrettyUserAgent(String userAgentString, String gwtUserAgent) throws IOException { MemcacheService mc = MemcacheServiceFactory.getMemcacheService(); UserAgentSummary userAgentSummary = null; // Get info out of memcache try { userAgentSummary = (UserAgentSummary) mc.get(userAgentString); } catch (RuntimeException e) { logger.log(Level.WARNING, "Exception getting value from memcache; possible serialVersionUID issue", e); } if (userAgentSummary != null) { return userAgentSummary; } else { // Next, try the datastore userAgentSummary = lookupUserAgentInDatastore(userAgentString); // Next, browserscope it if (userAgentSummary == null) { try { userAgentSummary = lookupUserAgentInBrowserScope(userAgentString, gwtUserAgent); } catch (Exception e) { logger.log(Level.SEVERE, "Unable to lookup new user agent in BrowserScope: " + userAgentString, e); } } // Browserscope lookup returned null or threw exception if (userAgentSummary == null) { userAgentSummary = new UserAgentSummary(userAgentString, null, gwtUserAgent); } // Store result in memcache if (userAgentSummary != null) { if (userAgentSummary.getPrettyUserAgent() != null) { Objectify ofy = ObjectifyService.begin(); ofy.put(userAgentSummary); } mc.put(userAgentString, userAgentSummary); } } return userAgentSummary; } /** * Lookup a human readable user agent in the datastore. * @param userAgentString raw user agent string * * @return human readable user agent or {@code null} */ public static UserAgentSummary lookupUserAgentInDatastore(String userAgentString) { Objectify ofy = ObjectifyService.begin(); com.googlecode.objectify.Query<UserAgentSummary> q = ofy.query(UserAgentSummary.class); q.filter("userAgentString", userAgentString); return q.get(); } /** * Lookup a human readable user agent via browserscope. * * @param userAgentString raw user agent string * @param gwtUserAgent GWT {code user.agent} property value * @return human readable user agent * @throws MalformedURLException maybe * @throws IOException maybe * @throws UnsupportedEncodingException maybe */ public static UserAgentSummary lookupUserAgentInBrowserScope(String userAgentString, String gwtUserAgent) throws MalformedURLException, IOException, UnsupportedEncodingException { URL url = new URL("http://www.browserscope.org/"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("User-Agent", userAgentString); ByteArrayInputStream content = (ByteArrayInputStream) connection.getContent(); byte[] buffer = new byte[BUFFER_SIZE]; ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE); try { while (true) { int byteCount = content.read(buffer); if (byteCount == -1) { break; } out.write(buffer, 0, byteCount); } String pageContent = out.toString("UTF-8"); Pattern pattern = Pattern.compile("<strong id=\"bs-cur-ua-summary\">(.*?)</strong>"); Matcher matcher = pattern.matcher(pageContent); if (matcher.find()) { String prettyUserAgent = matcher.group(1); return new UserAgentSummary(userAgentString, prettyUserAgent, gwtUserAgent); } else { sendEmail("browserscope failed to recognize " + userAgentString, pageContent); return null; } } finally { if (content != null) { content.close(); } } } }