Extracted ooo-user bits into separate library.
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.servlet;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.samskivert.depot.PersistenceContext;
|
||||
import com.samskivert.servlet.Site;
|
||||
import com.samskivert.servlet.SiteIdentifier;
|
||||
import com.samskivert.util.ArrayUtil;
|
||||
|
||||
import com.threerings.servlet.persist.DomainRecord;
|
||||
import com.threerings.servlet.persist.SiteIdentifierRepository;
|
||||
import com.threerings.servlet.persist.SiteRecord;
|
||||
|
||||
/**
|
||||
* Accomplishes the process of site identification based on a mapping from domains (e.g.
|
||||
* samskivert.com) to site identifiers that is maintained in a depot database table.
|
||||
*
|
||||
* <p> There are two tables, one that maps domains to site identifiers and another that maps site
|
||||
* identifiers to site strings. These are both loaded at construct time and refreshed periodically
|
||||
* in the course of normal operation.
|
||||
*
|
||||
* <p> Note that any of the calls to identify, lookup or enumerate site information can result in
|
||||
* the sites table being refreshed from the database which will take relatively much longer than
|
||||
* the simple hashtable lookup that the operations normally require. However, this happens only
|
||||
* once every 15 minutes and the circumstances in which the site identifier are normally used can
|
||||
* generally accomodate the extra 100 milliseconds or so that it is likely to take to reload the
|
||||
* (tiny) sites and domains tables from the database.
|
||||
*/
|
||||
public class DepotSiteIdentifier
|
||||
implements SiteIdentifier
|
||||
{
|
||||
/**
|
||||
* Constructs a debot site identifier.
|
||||
*/
|
||||
public DepotSiteIdentifier (PersistenceContext pctx)
|
||||
{
|
||||
this(pctx, DEFAULT_SITE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an identifier that will load data from the supplied connection provider and which
|
||||
* will use the supplied default site id instead of {@link #DEFAULT_SITE_ID}.
|
||||
*/
|
||||
public DepotSiteIdentifier (PersistenceContext pctx, int defaultSiteId)
|
||||
{
|
||||
this(pctx, defaultSiteId, DEFAULT_SITE_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an identifier that will load data from the supplied connection provider and which
|
||||
* will use the supplied default site id instead of {@link #DEFAULT_SITE_ID} and the supplied
|
||||
* default site string instead of {@link #DEFAULT_SITE_STRING}.
|
||||
*/
|
||||
public DepotSiteIdentifier (
|
||||
PersistenceContext pctx, int defaultSiteId, String defaultSiteString)
|
||||
{
|
||||
_repo = new SiteIdentifierRepository(pctx);
|
||||
refreshSiteData();
|
||||
_defaultSiteId = defaultSiteId;
|
||||
_defaultSiteString = defaultSiteString;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int identifySite (HttpServletRequest req)
|
||||
{
|
||||
checkReloadSites();
|
||||
String serverName = req.getServerName();
|
||||
|
||||
// scan for the mapping that matches the specified domain
|
||||
int msize = _mappings.size();
|
||||
for (int i = 0; i < msize; i++) {
|
||||
SiteMapping mapping = _mappings.get(i);
|
||||
if (serverName.endsWith(mapping.domain)) {
|
||||
return mapping.siteId;
|
||||
}
|
||||
}
|
||||
|
||||
// if we matched nothing, return the default id
|
||||
return _defaultSiteId;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public String getSiteString (int siteId)
|
||||
{
|
||||
checkReloadSites();
|
||||
Site site = _sitesById.get(siteId);
|
||||
if (site == null) {
|
||||
site = _sitesById.get(_defaultSiteId);
|
||||
}
|
||||
return (site == null) ? _defaultSiteString : site.siteString;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public int getSiteId (String siteString)
|
||||
{
|
||||
checkReloadSites();
|
||||
Site site = _sitesByString.get(siteString);
|
||||
return (site == null) ? _defaultSiteId : site.siteId;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public Iterator<Site> enumerateSites ()
|
||||
{
|
||||
checkReloadSites();
|
||||
return _sitesById.values().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new site into the site table and into this mapping.
|
||||
*/
|
||||
public Site insertNewSite (String siteString)
|
||||
{
|
||||
return insertNewSite(siteString, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new site into the site table and into this mapping.
|
||||
*/
|
||||
public Site insertNewSite (String siteString, int siteId)
|
||||
{
|
||||
if (_sitesByString.containsKey(siteString) ||
|
||||
(siteId > 0 && _sitesById.containsKey(siteId))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// add it to the db
|
||||
Site site = _repo.insertNewSite(siteString, siteId);
|
||||
|
||||
// add it to our two mapping tables, taking care to avoid causing enumerateSites() to choke
|
||||
Map<String, Site> newStrings = Maps.newHashMap(_sitesByString);
|
||||
Map<Integer, Site> newIds = Maps.newHashMap(_sitesById);
|
||||
newIds.put(site.siteId, site);
|
||||
newStrings.put(site.siteString, site);
|
||||
_sitesByString = newStrings;
|
||||
_sitesById = newIds;
|
||||
|
||||
return site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new domain into the domain table and into this mapping.
|
||||
*/
|
||||
public void insertNewDomain (String domain, int siteId)
|
||||
{
|
||||
// scan for the mapping that matches the specified domain
|
||||
int msize = _mappings.size();
|
||||
for (int i = 0; i < msize; i++) {
|
||||
SiteMapping mapping = _mappings.get(i);
|
||||
if (mapping.domain.equals(domain)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// add it to the db
|
||||
_repo.insertNewDomain(domain, siteId);
|
||||
|
||||
// add it to our two mapping table, taking care to avoid causing enumerateSites() to choke
|
||||
List<SiteMapping> mappings = Lists.newArrayList();
|
||||
mappings.addAll(_mappings);
|
||||
mappings.add(new SiteMapping(siteId, domain));
|
||||
|
||||
Collections.sort(mappings, SiteMapping.BY_SPECIFICITY);
|
||||
_mappings = mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if we should reload our sites information from the sites table.
|
||||
*/
|
||||
protected void checkReloadSites ()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
boolean reload = false;
|
||||
synchronized (this) {
|
||||
reload = (now - _lastReload > RELOAD_INTERVAL);
|
||||
if (reload) {
|
||||
_lastReload = now;
|
||||
}
|
||||
}
|
||||
if (reload) {
|
||||
refreshSiteData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the cached site information.
|
||||
*/
|
||||
public void refreshSiteData ()
|
||||
{
|
||||
List<SiteRecord> siteRecords = _repo.loadSites();
|
||||
Map<Integer,Site> sites = Maps.newHashMap();
|
||||
Map<String,Site> strings = Maps.newHashMap();
|
||||
for (SiteRecord record : siteRecords) {
|
||||
Site site = record.toSite();
|
||||
sites.put(record.siteId, site);
|
||||
strings.put(record.siteString, site);
|
||||
}
|
||||
_sitesById = sites;
|
||||
_sitesByString = strings;
|
||||
|
||||
List<DomainRecord> domainRecords = _repo.loadDomains();
|
||||
List<SiteMapping> mappings = Lists.newArrayList();
|
||||
for (DomainRecord record : domainRecords) {
|
||||
mappings.add(new SiteMapping(record.siteId, record.domain));
|
||||
}
|
||||
|
||||
Collections.sort(mappings, SiteMapping.BY_SPECIFICITY);
|
||||
_mappings = mappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to track domain to site identifier mappings.
|
||||
*/
|
||||
protected static class SiteMapping
|
||||
{
|
||||
/**
|
||||
* Sorts site mappings from most specific (www.yahoo.com) to least specific (yahoo.com).
|
||||
*/
|
||||
public static final Comparator<SiteMapping> BY_SPECIFICITY = new Comparator<SiteMapping>() {
|
||||
public int compare (SiteMapping one, SiteMapping two) {
|
||||
return one._rdomain.compareTo(two._rdomain);
|
||||
}
|
||||
};
|
||||
|
||||
/** The domain to match. */
|
||||
public String domain;
|
||||
|
||||
/** The site identifier for the associated domain. */
|
||||
public int siteId;
|
||||
|
||||
public SiteMapping (int siteId, String domain) {
|
||||
this.siteId = siteId;
|
||||
this.domain = domain;
|
||||
byte[] bytes = domain.getBytes();
|
||||
ArrayUtil.reverse(bytes);
|
||||
_rdomain = new String(bytes);
|
||||
}
|
||||
|
||||
@Override // from Object
|
||||
public String toString () {
|
||||
return "[" + domain + " => " + siteId + "]";
|
||||
}
|
||||
|
||||
protected String _rdomain;
|
||||
}
|
||||
|
||||
/** The repository through which we load up site identifier information. */
|
||||
protected SiteIdentifierRepository _repo;
|
||||
|
||||
/** The site id to return if we cannot identify the site from our table data. */
|
||||
protected int _defaultSiteId;
|
||||
|
||||
/** The site string to return if we cannot identify the site from our table data. */
|
||||
protected String _defaultSiteString;
|
||||
|
||||
/** The list of domain to site identifier mappings ordered from most specific domain to least
|
||||
* specific. */
|
||||
protected volatile List<SiteMapping> _mappings = Lists.newArrayList();
|
||||
|
||||
/** The mapping from integer site identifiers to string site identifiers. */
|
||||
protected volatile Map<Integer, Site> _sitesById = Maps.newHashMap();
|
||||
|
||||
/** The mapping from string site identifiers to integer site identifiers. */
|
||||
protected volatile Map<String, Site> _sitesByString = Maps.newHashMap();
|
||||
|
||||
/** Used to periodically reload our site data. */
|
||||
protected long _lastReload;
|
||||
|
||||
/** Reload our site data every 15 minutes. */
|
||||
protected static final long RELOAD_INTERVAL = 15 * 60 * 1000L;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.servlet.persist;
|
||||
|
||||
import com.samskivert.depot.Key;
|
||||
import com.samskivert.depot.PersistentRecord;
|
||||
import com.samskivert.depot.expression.ColumnExp;
|
||||
import com.samskivert.depot.annotation.Column;
|
||||
import com.samskivert.depot.annotation.Entity;
|
||||
import com.samskivert.depot.annotation.Id;
|
||||
import com.samskivert.servlet.SiteIdentifier;
|
||||
|
||||
/**
|
||||
* Represents a domain mapping known to a {@link SiteIdentifier}.
|
||||
*/
|
||||
@Entity(name="domains")
|
||||
public class DomainRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<DomainRecord> _R = DomainRecord.class;
|
||||
public static final ColumnExp<String> DOMAIN = colexp(_R, "domain");
|
||||
public static final ColumnExp<Integer> SITE_ID = colexp(_R, "siteId");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
public static final int SCHEMA_VERSION = 1;
|
||||
|
||||
/** The domain. */
|
||||
@Id @Column(length=128)
|
||||
public String domain;
|
||||
|
||||
/** The site identifier. */
|
||||
public int siteId;
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Create and return a primary {@link Key} to identify a {@link DomainRecord}
|
||||
* with the supplied key values.
|
||||
*/
|
||||
public static Key<DomainRecord> getKey (String domain)
|
||||
{
|
||||
return newKey(_R, domain);
|
||||
}
|
||||
|
||||
/** Register the key fields in an order matching the getKey() factory. */
|
||||
static { registerKeyFields(DOMAIN); }
|
||||
// AUTO-GENERATED: METHODS END
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.servlet.persist;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.depot.DepotRepository;
|
||||
import com.samskivert.depot.PersistenceContext;
|
||||
import com.samskivert.depot.PersistentRecord;
|
||||
import com.samskivert.servlet.Site;
|
||||
|
||||
/**
|
||||
* Depot implements of a site identifier repository.
|
||||
*/
|
||||
@Singleton
|
||||
public class SiteIdentifierRepository extends DepotRepository
|
||||
{
|
||||
/**
|
||||
* Creates the repository.
|
||||
*/
|
||||
@Inject public SiteIdentifierRepository (PersistenceContext ctx)
|
||||
{
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the sites.
|
||||
*/
|
||||
public List<SiteRecord> loadSites ()
|
||||
{
|
||||
return findAll(SiteRecord.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the domains.
|
||||
*/
|
||||
public List<DomainRecord> loadDomains ()
|
||||
{
|
||||
return findAll(DomainRecord.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new site.
|
||||
*/
|
||||
public Site insertNewSite (String siteString)
|
||||
{
|
||||
return insertNewSite(siteString, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new site.
|
||||
*/
|
||||
public Site insertNewSite (String siteString, int siteId)
|
||||
{
|
||||
SiteRecord record = new SiteRecord();
|
||||
record.siteString = siteString;
|
||||
record.siteId = siteId;
|
||||
insert(record);
|
||||
return record.toSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new domain.
|
||||
*/
|
||||
public void insertNewDomain (String domain, int siteId)
|
||||
{
|
||||
DomainRecord record = new DomainRecord();
|
||||
record.domain = domain;
|
||||
record.siteId = siteId;
|
||||
insert(record);
|
||||
}
|
||||
|
||||
@Override // documentation inherited
|
||||
protected void getManagedRecords (Set<Class<? extends PersistentRecord>> classes)
|
||||
{
|
||||
classes.add(DomainRecord.class);
|
||||
classes.add(SiteRecord.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.servlet.persist;
|
||||
|
||||
import com.samskivert.depot.Key;
|
||||
import com.samskivert.depot.PersistentRecord;
|
||||
import com.samskivert.depot.expression.ColumnExp;
|
||||
import com.samskivert.depot.annotation.Column;
|
||||
import com.samskivert.depot.annotation.Entity;
|
||||
import com.samskivert.depot.annotation.GeneratedValue;
|
||||
import com.samskivert.depot.annotation.GenerationType;
|
||||
import com.samskivert.depot.annotation.Id;
|
||||
|
||||
import com.samskivert.servlet.Site;
|
||||
import com.samskivert.servlet.SiteIdentifier;
|
||||
|
||||
/**
|
||||
* Represents a site mapping known to a {@link SiteIdentifier}.
|
||||
*/
|
||||
@Entity(name="sites")
|
||||
public class SiteRecord extends PersistentRecord
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
public static final Class<SiteRecord> _R = SiteRecord.class;
|
||||
public static final ColumnExp<Integer> SITE_ID = colexp(_R, "siteId");
|
||||
public static final ColumnExp<String> SITE_STRING = colexp(_R, "siteString");
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
public static final int SCHEMA_VERSION = 1;
|
||||
|
||||
/** The site's unique identifier. */
|
||||
@Id @GeneratedValue(strategy=GenerationType.AUTO)
|
||||
public int siteId;
|
||||
|
||||
/** The site's human readable identifier (i.e., "monkeybutter"). */
|
||||
@Column(length=24)
|
||||
public String siteString;
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Create and return a primary {@link Key} to identify a {@link SiteRecord}
|
||||
* with the supplied key values.
|
||||
*/
|
||||
public static Key<SiteRecord> getKey (int siteId)
|
||||
{
|
||||
return newKey(_R, siteId);
|
||||
}
|
||||
|
||||
/** Register the key fields in an order matching the getKey() factory. */
|
||||
static { registerKeyFields(SITE_ID); }
|
||||
// AUTO-GENERATED: METHODS END
|
||||
|
||||
/**
|
||||
* Creates a Site from this record.
|
||||
*/
|
||||
public Site toSite ()
|
||||
{
|
||||
Site site = new Site();
|
||||
site.siteId = siteId;
|
||||
site.siteString = siteString;
|
||||
return site;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user