Skip to content

Commit

Permalink
Rearchitected the DSP Configuration framework
Browse files Browse the repository at this point in the history
DemandSideServer must read in from configuration file (or Database)
which exchanges are authorized to send BidRequest feed (a.k.a. supply
feed), what Content-Type will be in use and other SSP specific
parameters. It must also read in the list of Advertisers using this DSP
as a bidding front end, which advertisers have Seats with which
exchanges, and any other Advertiser specific information. All this has
been rearchitected using the DAO pattern to remove hard dependency on
config or property files. Initial implementation of the DemandSideDAO
interface is for a JsonFileBackedDAO.
  • Loading branch information
chompi committed Mar 12, 2013
1 parent ce6aaf5 commit 038ba46
Show file tree
Hide file tree
Showing 4 changed files with 340 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2010, The OpenRTB Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the OpenRTB nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.openrtb.dsp.client;

import java.io.File;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.annotate.JsonPropertyOrder;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.openrtb.dsp.intf.model.DSPException;
import org.openrtb.dsp.intf.model.DemandSideDAO;
import org.openrtb.dsp.intf.model.RTBAdvertiser;
import org.openrtb.dsp.intf.model.RTBExchange;

/* This class reads the initial DSP configuration from a JSON file named
* "properties.json" found in the class path provided in its load(..) method
* The json file is expected to have the following format :
* { "serverport" : 8080, "requestTO" : 120, "offerTO" : 200,
* "exchanges" : [ { "orgname" : "BigAdExchange", "rtburl" : "https://bigadex.com/rtb", "rtbctype" : "application/json", "batchurl" : "https://bigadex.com/blocklist", "batchctype" : "application/json" }, .. {} ],
* "advertisers" : [ { "orgname" : "BigBrand", "nurl" : "https://bigbrand-adserver.com/nurl", "categories": [ "cat1", "cat2", ... ], "seats" : [ { "BigAdExchange" : "SeatID" }, ... {} }, ... {} ]
* }
*/


public class JsonFileBackedDAO implements DemandSideDAO {
private String dbLocation;

private final ObjectMapper mapper = new ObjectMapper();

// data
private final ConcurrentMap<String, Integer> properties = new ConcurrentHashMap<String, Integer>();
private final ConcurrentMap<String, RTBExchange> exchanges = new ConcurrentHashMap<String, RTBExchange>();
private final ConcurrentMap<String, RTBAdvertiser> advertisers = new ConcurrentHashMap<String, RTBAdvertiser>();

// encapsulate all the config properties in the Json file backed data store in a temporary class
// this is used by a Jackson object mapper to load data from a JSON file.
// this is not designed to for concurrent access from multiple threads simultaneous,
// hence loadData and persistData are the only methods (synchronized) that should
// use this internal class

@JsonSerialize(include=Inclusion.NON_DEFAULT)
@JsonPropertyOrder({"serverport", "requestTO", "offerTO", "exchanges", "advertisers"})
class DSPProps {
@JsonProperty("serverport")
int dspServerPort;

@JsonProperty("requestTO")
int defaultReqTimeout;

@JsonProperty("offerTO")
int defaultOfferTimeout;

@JsonProperty("exchanges")
List<RTBExchange> exchanges;

@JsonProperty("advertisers")
List<RTBAdvertiser> advertisers;

@JsonProperty("serverport")
int getDspServerPort() {
return dspServerPort;
}
void setDspServerPort(int dspServerPort) {
this.dspServerPort = dspServerPort;
}

@JsonProperty("requestTO")
int getDefaultReqTimeout() {
return defaultReqTimeout;
}
void setDefaultReqTimeout(int defaultReqTimeout) {
this.defaultReqTimeout = defaultReqTimeout;
}

@JsonProperty("offerTO")
int getDefaultOfferTimeout() {
return defaultOfferTimeout;
}
void setDefaultOfferTimeout(int defaultOfferTimeout) {
this.defaultOfferTimeout = defaultOfferTimeout;
}

@JsonProperty("exchanges")
List<RTBExchange> getExchanges() {
return exchanges;
}
void setExchanges(List<RTBExchange> exchanges) {
this.exchanges = exchanges;
}

@JsonProperty("advertisers")
List<RTBAdvertiser> getAdvertisers() {
return advertisers;
}
void setAdvertisers(List<RTBAdvertiser> advertisers) {
this.advertisers = advertisers;
}
}

public synchronized void reload() throws DSPException {
this.loadData(dbLocation);
}

// the get method of ConcurrentMap is not blocking, hence make this synchronized
@Override
public synchronized long getServerPort() {
return properties.get("server_port");
}

@Override
public ConcurrentMap<String, RTBExchange> getExchanges() {
return exchanges;
}

@Override
public ConcurrentMap<String, RTBAdvertiser> getAdvertisers() {
return advertisers;
}

@Override
public synchronized void loadData(String dbLocation) throws DSPException {
try {
if ((dbLocation != null) && (dbLocation != "")) {
this.dbLocation = dbLocation;
} else { // otherwise use a default location and filename relative to this class
this.dbLocation = getClass().getResource("dspConf.json").getFile();
}
mapper.enableDefaultTyping();
// read the properties as an object using ObjectMapper
DSPProps props = mapper.readValue(new File(this.dbLocation), DSPProps.class);

this.properties.put("server_port", props.dspServerPort);
this.properties.put("request_timeout", props.defaultReqTimeout);
this.properties.put("offer_timeout", props.defaultOfferTimeout);

for (RTBExchange ex : props.exchanges) {
this.exchanges.put(ex.getOrgName(), ex);
}

for (RTBAdvertiser adv : props.advertisers) {
this.advertisers.put(adv.getLandingPage(), adv);
}

} catch (Exception e) {
throw new DSPException(e.getMessage());
}
}

@Override
public synchronized long getDefaultTimeout(String string) {
return properties.get(string);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,70 +31,17 @@
*/
package org.openrtb.dsp.intf.model;

import java.util.List;
import java.util.concurrent.ConcurrentMap;


import org.mortbay.jetty.Connector;
import org.openrtb.common.api.Advertiser;
/**
*
*/
public class DSPConfig {
public static DSPConfig theDSPConfig;

static String pathToConfigFile = ""; // TODO if no path is provided, initialize a default path name for config file
List<SupplySidePlatform> sspExchanges;
int defaultRequestTimerMs;

/**
*
*/
public DSPConfig(String pathToConfigFile) {
readConfig(pathToConfigFile);
}

public static DSPConfig newConfig(String pathToConfigFile) {
// TODO Auto-generated constructor stub
if (pathToConfigFile == null || pathToConfigFile == "")
throw new IllegalArgumentException("Illegal pathToConfigFile");
DSPConfig.theDSPConfig = new DSPConfig(pathToConfigFile);
return DSPConfig.theDSPConfig;
}

public static DSPConfig get() { return DSPConfig.theDSPConfig; }

// Config file is a JSON encoded file
void readConfig(String pathToConfigFile) {
// TODO read config here
}

/**
* @return
*/
public java.util.List<Advertiser> getAdvertisers() {
// TODO Auto-generated method stub
return null;
}

/**
* @return
*/
public java.util.List<SupplySidePlatform> getExchanges() {
// TODO Auto-generated method stub
return null;
}

/**
* @return
*/
public Connector getServerPort() {
// TODO Auto-generated method stub
return null;
}

/**
* @return
*/
public int getDefaultRequestTimerMs() {
return (defaultRequestTimerMs=120);
}
public interface DemandSideDAO {
public long getServerPort();
public long getDefaultTimeout(String string);
public ConcurrentMap<String, RTBExchange> getExchanges();
public ConcurrentMap<String, RTBAdvertiser> getAdvertisers();
public void loadData(String dbLocation) throws DSPException;
}

Loading

0 comments on commit 038ba46

Please sign in to comment.