Java code to work with MillennialMedia, SMAATO and NexAge web advertising APIs

Dec 18, 2010 by

I wrote yesterday about my (mostly) negative experience working with these companies, but some of you might give it a try and maybe your results will be much better or maybe things have actually improved lately. In any case, the code here should get you up to speed covering most of the dirty work of working with these companies’ APIs.

I will post 4 classes here:

  • AdGrabber -  main abstract class to give all other specific grabbers structure and save some repeating code
  • MillenialAdGrabber - MillennialMedia specific grabber
  • NexAgeAdGrabber - right, NexAge specific grabber
  • SomaAdGrabber - the SMAATO ad grabber. Their ad platform is dubbed SOMA, hence the name

If you are using this code, please give an attribution when applicable with a link to this post.

AdGrabber


/**
 * Abstract class for ad unit grabbing. Inheriting subclasses will implement ad serving platform specific web service interfaces.
 *
 * Copyright: Creative Common Attribution http://creativecommons.org/licenses/by/3.0/
 */

public abstract class AdGrabber {

   protected HashTableExt _adFieldPropertyBag;

   public AdGrabber(HashTableExt adFieldPropertyBag) {
      if (adFieldPropertyBag == null) {
         _adFieldPropertyBag = new HashTableExt();
      } else {
         _adFieldPropertyBag = adFieldPropertyBag;
      }
   }

   /**
    * Inheriting classes have to implement the grabbing and the parsing of the ad from the appropriate ad serving web service
    *
    * @return
    *          - an {@link Ad} object populated with the values parsed from the appropriate ad serving web service
    */

   public Ad grabTheAd() {
      try {
         Document adResponseDoc = StreamUtils.parseRemoteXML(getRequestURL(_adFieldPropertyBag));
         return parseServerResponse(adResponseDoc);
      } catch (Exception e) {
         //Log error
      }
      return new EmptyAd();
   }

   protected abstract String getRequestURL(HashTableExt adFieldPropertyBag);

   protected Ad parseServerResponse(Document doc) {
      Ad ad = null;

      if (doc == null) {
         return ad; // basically = return null
      }

      try {
         if (gotProperAd(doc)) {
            if (shouldParseAsImageAd(doc)) {
               ad = parseImageAd(doc);
            } else {
               ad = parseTextAd(doc);
            }

            ad.setNetwork(StringUtils.getClassName(this));

         } else { // did NOT get a proper ad
            ad = new EmptyAd();
         }

      } catch (Throwable t) {
         //Log error
      }

      return ad;
   }

   protected abstract boolean gotProperAd(Document doc) throws Exception;

   protected abstract TextAd parseTextAd(Document doc) throws Exception;

   protected abstract ImageAd parseImageAd(Document doc) throws Exception;

   protected abstract boolean shouldParseAsImageAd(Document doc) throws Exception;

}

MillenialAdGrabber


/**
 * Ad grabber that interfaces with Quattro Wireless service
 *
 * Copyright: Creative Common Attribution http://creativecommons.org/licenses/by/3.0/
 */

public class MillenialAdGrabber extends AdGrabber {

   /* ------------------------------------------         PUBLISHING CONSTANTS       ------------------------------------------*/

   private static final String APP_ID = "YOUR_APP_ID";

   /* ------------------------------------------                                 ------------------------------------------*/

   public MillenialAdGrabber(HashTableExt adFieldPropertyBag) {
      super(adFieldPropertyBag);
   }

   /**
    *
    * @param adFieldPropertyBag
    * @return
    *          - The URL to be sent to the advertising web service
    */

   protected String getRequestURL(HashTableExt adFieldPropertyBag) {
      StringBuffer sb = new StringBuffer("http://ads.mp.mydas.mobi/getAd.php5?");

      sb.append("apid=" + APP_ID);

      sb.append("&auid=" + BBInfo.getUniqueDeviceHash());

      sb.append("&uip=" + URLTools.urlEncodeBBForum(BBInfo.getIP())); // device IP

      sb.append("&ua=" + URLTools.urlEncodeBBForum(BBInfo.getUserAgent())); // USER AGENT

      return sb.toString();
   }

   /* ------------------------------------------------            AD response PARSING        ------------------------------------------------ */

   protected boolean gotProperAd(Document doc) throws Exception {
      NodeList nl = ((NodeList) doc.getElementsByTagName("ad"));
      if (nl != null) {
         return nl.getLength() > 0;
      } else {
         return false;
      }
   }

   protected boolean shouldParseAsImageAd(Document doc) throws Exception {
      if (BBInfo.isTallScreen() || _adFieldPropertyBag.get(AdAllocManager.FETCH_IMAGE_ADS) != null) {
         if (!StringUtils.isEmpty(XML.getTextFromNode(doc.getElementsByTagName("url").item(0)))) {
            return true;
         }
      }

      return false;
   }

   protected TextAd parseTextAd(Document doc) throws Exception {
      TextAd ad = new TextAd();
      parseCommonAdValues(ad, doc);

      //parse the TEXT of the banner
      ad.setText(XML.getTextFromNode(doc.getElementsByTagName("altText").item(0)));

      return ad;
   }

   protected ImageAd parseImageAd(Document doc) throws Exception {
      ImageAd ad = new ImageAd();
      parseCommonAdValues(ad, doc);

      //parse the IMAGE of the banner
      String imageURL = XML.getNodeTextCumulative(doc.getElementsByTagName("url"));
      EncodedImage bannerImage = EncodedImage.createEncodedImage(StreamUtils.readRemoteStream(imageURL).getBytes(), 0, -1);
      ad.setImage(bannerImage);

      return ad;
   }

   /**
    * Parse common values from the responce like the:
    *  - tracking pixels/beacons
    *  - the click URL
    *
    * @param ad
    * @param doc
    */

   private void parseCommonAdValues(Ad ad, Document doc) throws Exception {
      // CLICK URL
      ad.setClickURL(XML.getNodeTextCumulative(doc.getElementsByTagName("clickUrl")));
   }

}

NexAgeAdGrabber


/**
 * Copyright: Creative Common Attribution http://creativecommons.org/licenses/by/3.0/
 */

public class NexAgeAdGrabber extends AdGrabber {

   public NexAgeAdGrabber(HashTableExt adFieldPropertyBag) {
      super(adFieldPropertyBag);
   }

   /* ------------------------------------------         PUBLISHING CONSTANTS       ------------------------------------------*/

   private static final String SITE_ID = "YOUR_SITE_ID";

   /* ------------------------------------------                                 ------------------------------------------*/

   /**
    *
    * @param adFieldPropertyBag
    * @return
    *          - The URL to be sent to the advertising web service
    */

   protected String getRequestURL(HashTableExt adFieldPropertyBag) {
      StringBuffer sb = new StringBuffer("http://admax.nexage.com/adServe?");

      sb.append("dcn=" + SITE_ID);

      sb.append("&pos=" + getAdPosition(adFieldPropertyBag));

      sb.append("&ip=" + URLTools.urlEncodeBBForum(BBInfo.getIP())); // device IP

      sb.append("&ua=" + URLTools.urlEncodeBBForum(BBInfo.getUserAgent())); // device USER AGENT

      sb.append("&f=" + "xml");

      sb.append("&u(id)=" + BBInfo.getUniqueDeviceHash());

      return sb.toString();
   }

   private static String getAdPosition(HashTableExt adFieldPropertyBag) {
      if (adFieldPropertyBag != null && adFieldPropertyBag.get(AdAllocManager.AD_POSITION) != null) { //if we specified the ad position - return the specified ad position
         return (String) adFieldPropertyBag.get(AdAllocManager.AD_POSITION);
      } else {
         // otherwise, use the "imageonly" or "textonly", depending on the display size, or manual override
         return (BBInfo.isTallScreen() || (adFieldPropertyBag != null && adFieldPropertyBag.get(AdAllocManager.FETCH_IMAGE_ADS) != null)) ? "imageonly" : "textonly";
      }
   }

   protected boolean gotProperAd(Document doc) throws Exception {
      Node adsNode = doc.getElementsByTagName("ads").item(0);
      if (adsNode != null) {
         int adCount = Integer.parseInt(XML.getAttributeValue(adsNode, "count"));
         return adCount > 0;
      } else {
         return false;
      }
   }

   protected ImageAd parseImageAd(Document doc) throws Exception {
      ImageAd ad = new ImageAd();
      parseCommonAdValues(ad, doc);

      //parse the IMAGE of the banner
      String imageURL = XML.getAttributeValue(doc.getElementsByTagName("ad:content").item(0), "url");

      EncodedImage bannerImage = EncodedImage.createEncodedImage(StreamUtils.readRemoteStream(imageURL).getBytes(), 0, -1);
      ad.setImage(bannerImage);

      return ad;
   }

   protected TextAd parseTextAd(Document doc) throws Exception {
      TextAd ad = new TextAd();
      parseCommonAdValues(ad, doc);

      //parse the TEXT of the banner
      ad.setText(XML.getTextFromNode(doc.getElementsByTagName("ad:text").item(0)));

      return ad;
   }

   protected boolean shouldParseAsImageAd(Document doc) throws Exception {
      Node node = doc.getElementsByTagName("ad:group").item(0);
      String adType = XML.getAttributeValue(node, "type");

      return adType.toLowerCase().compareTo("banner") == 0;
   }

   /**
    * Parse common values from the responce like the:
    *  - tracking pixels/beacons
    *  - the click URL
    *
    * @param ad
    * @param doc
    */

   private void parseCommonAdValues(Ad ad, Document doc) {
      // CLICK URL
      ad.setClickURL(XML.getTextFromNode(doc.getElementsByTagName("link").item(0)));

      // TRACKING PIXEL URLs - get only ones from the FIRST  node (in case there are more than 1 ad in the response, only the first should be processed and the rest ignored)
      Node adEventsNode = doc.getElementsByTagName("ad:events").item(0);
      NodeList nl = adEventsNode.getChildNodes();

      for (int i = 0; i < nl.getLength(); i++) {
         // If the tracking is of the "display" type - it is a tracking PIXEL URL
         if (XML.getAttributeValue(nl.item(i), "type").toLowerCase().compareTo("display") == 0) {
            String nodeName = nl.item(i).getFirstChild().getNodeName();
            if (nodeName.toLowerCase().compareTo("link") == 0) {
               String trackingPx = XML.getTextFromNode(nl.item(i).getFirstChild());
               ad.addTrackingPixelURL(trackingPx);
            }
         }
         // otherwise, if it is of the "click" type - it is a CLICK tracking URL
         else if (XML.getAttributeValue(nl.item(i), "type").toLowerCase().compareTo("click") == 0) {
            String nodeName = nl.item(i).getFirstChild().getNodeName();
            if (nodeName.toLowerCase().compareTo("link") == 0) {
               String trackingPx = XML.getTextFromNode(nl.item(i).getFirstChild());
               ad.addClickTrackingURL(trackingPx);
            }
         }
      }
   }

}

SomaAdGrabber


/**
 * Copyright © Comitic Software
 */

public class SOMAAdGrabber extends AdGrabber {

   public SOMAAdGrabber(HashTableExt adFieldPropertyBag) {
      super(adFieldPropertyBag);
   }

   /* ------------------------------------------         PUBLISHING CONSTANTS       ------------------------------------------*/
   private static final String AD_SPACE = "YOUR_AD_SPACE";

   private static final String PUBLISHER_ID = "YOUR_PUB_ID";

   private static final String SOMA_API_VER = "somaapi-318"; //This is the version of the API I used

   /* ------------------------------------------                                 ------------------------------------------*/

   /**
    *
    * @param overrideImageAds
    * @return
    *          - The URL to be sent to the advertising web service
    */

   protected String getRequestURL(HashTableExt adFieldPropertyBag) {
      StringBuffer sb = new StringBuffer("http://soma.smaato.net/oapi/reqAd.jsp?");

      sb.append("adspace=" + AD_SPACE);

      sb.append("&pub=" + PUBLISHER_ID);

      sb.append("&client=" + SOMA_API_VER);

      sb.append("&devip=" + URLTools.urlEncodeBBForum(BBInfo.getIP())); // device IP

      sb.append("&device=" + URLTools.urlEncodeBBForum(BBInfo.getUserAgent())); // device USER AGENT

      String adMode = (BBInfo.isTallScreen() || (adFieldPropertyBag!=null && adFieldPropertyBag.get(AdAllocManager.FETCH_IMAGE_ADS) != null)) ? "all" : "txt"; // "all" = IMAGE and TEXT; "txt" = TEXT only
      sb.append("&format=" + adMode);

      sb.append("&ownid=" + BBInfo.getUniqueDeviceHash());

      sb.append("&responce=" + "xml");

      return sb.toString();
   }

   protected boolean gotProperAd(Document doc) throws Exception {
      //return ((NodeList) doc.getElementsByTagName("ads")).getLength() > 0 && canProcessTheAd(doc);
      NodeList nl = ((NodeList) doc.getElementsByTagName("ads"));
      if (nl != null) {
         return nl.getLength() > 0  && canProcessTheAd(doc);
      } else {
         return false;
      }
   }

   private boolean canProcessTheAd(Document doc) {
      Node node = doc.getElementsByTagName("action").item(0);
      String actionType = XML.getAttributeValue(node, "type");
      return actionType.toLowerCase().compareTo("link") == 0;
   }

   protected ImageAd parseImageAd(Document doc) throws Exception {
      ImageAd ad = new ImageAd();
      parseCommonAdValues(ad, doc);

      //parse the IMAGE of the banner
      String imageURL = XML.getNodeTextCumulative(doc.getElementsByTagName("link"));

      EncodedImage bannerImage = EncodedImage.createEncodedImage(StreamUtils.readRemoteStream(imageURL).getBytes(), 0, -1);
      ad.setImage(bannerImage);

      return ad;
   }

   protected TextAd parseTextAd(Document doc) throws Exception {
      TextAd ad = new TextAd();
      parseCommonAdValues(ad, doc);

      //parse the TEXT of the banner
      ad.setText(XML.getTextFromNode(doc.getElementsByTagName("adtext").item(0)));

      return ad;
   }

   protected boolean shouldParseAsImageAd(Document doc) throws Exception {
      Node node = doc.getElementsByTagName("ad").item(0);
      String adType = XML.getAttributeValue(node, "type");

      return adType.toLowerCase().compareTo("img") == 0;

   }

   /**
    * Parse common values from the responce like the:
    *  - tracking pixels/beacons
    *  - the click URL
    *
    * @param ad
    * @param doc
    */

   private void parseCommonAdValues(Ad ad, Document doc) {
      // CLICK URL
      ad.setClickURL(XML.getAttributeValue(doc.getElementsByTagName("action").item(0), "target"));

      //TRACKING PIXEL URLs = the BEACONs as they are named in SMAATO network
      NodeList nl = doc.getElementsByTagName("beacon");
      for (int i = 0; i < nl.getLength(); i++) {
         String trackingPx = XML.getTextFromNode(nl.item(i));
         ad.addTrackingPixelURL(trackingPx);
      }
   }

}

Please let me know in the comments if you find this useful!


  • Alex

    Hey I’ve been doing research on advertising possibilities on blackberry and this code you posted seems really useful and would like to give it a shot in my app.
    But I noticed many of the classes here are missing, is it possible for you to share that code too? You can contact me in the email attached to this post.

    Thanks for the effort and for helping the bb community!

    • http://www.inteism.com/ inteist

      Unfortunately I will not be able to share the missing code. The code that you see here does most of the work. You just need to fill in the gaps which should not be a big deal.

      • Alex

        Ok, I understand. I saw that smaato offer a sdk now and I downloaded and I am testing it but it still says that there are no ads available currently for my app, in the admin panel I have added and configured everything properly. Are you aware if there is some kind of initial time to wait(a few hours, a day) before the ads start to be available for my app?