name.persistent.behaviours.RemoteDomainSupport.java Source code

Java tutorial

Introduction

Here is the source code for name.persistent.behaviours.RemoteDomainSupport.java

Source

/*
 * Copyright (c) Zepheira LLC, Some rights reserved.
 * 
 * Source code developed for this project is licensed under the Apache
 * License, Version 2.0. See the file LICENSE.txt for details.
 */
package name.persistent.behaviours;

import info.aduna.net.ParsedURI;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import name.persistent.concepts.RemoteDomain;
import name.persistent.concepts.Service;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.BasicStatusLine;
import org.openrdf.http.object.client.HTTPObjectClient;
import org.openrdf.http.object.exceptions.BadGateway;
import org.openrdf.http.object.exceptions.GatewayTimeout;
import org.openrdf.http.object.exceptions.InternalServerError;
import org.openrdf.http.object.threads.ManagedExecutors;
import org.openrdf.repository.object.RDFObject;

/**
 * Proxies PURL request to a remote PURL server.
 * 
 * @author James Leigh
 */
public abstract class RemoteDomainSupport extends MirroredDomainSupport implements RemoteDomain, RDFObject {
    private static final String PROTOCOL = "1.1";
    private static final String VIA;
    static {
        String host = "localhost";
        try {
            host = InetAddress.getLocalHost().getCanonicalHostName();
        } catch (UnknownHostException e) {
            // ignore
        }
        VIA = PROTOCOL + " " + host;
    }
    private static Map<InetSocketAddress, Boolean> blackList = new ConcurrentHashMap<InetSocketAddress, Boolean>();
    static {
        ManagedExecutors.getTimeoutThreadPool().scheduleWithFixedDelay(new Runnable() {
            public void run() {
                blackList.clear();
            }
        }, 1, 4, TimeUnit.HOURS);
    }

    private static ThreadLocal<Random> random = new ThreadLocal<Random>() {
        protected Random initialValue() {
            return new Random(System.nanoTime());
        }
    };

    @Override
    protected Object getReloadGraph() {
        return getPurlServicedBy();
    }

    @Override
    public HttpResponse resolvePURL(String source, String qs, String accept, String language, Set<String> via)
            throws IOException, InterruptedException {
        stayFresh();
        return resolveRemotePURL(source, qs, accept, language, via, true, null);
    }

    private HttpResponse resolveRemotePURL(String source, String qs, String accept, String language,
            Set<String> via, boolean useBlackList, HttpResponse bad) throws IOException, InterruptedException {
        List<InetSocketAddress> blacklisted = getBlackListing(useBlackList);
        Collection<List<Service>> records = getAllPURLServices();
        for (List<Service> services : records) {
            InetSocketAddress addr = pickService(services);
            if (addr != null) {
                HttpResponse resp = resolveRemotePURL(addr, source, qs, accept, language, via);
                if (resp == null)
                    continue;
                StatusLine status = resp.getStatusLine();
                if (status.getStatusCode() >= 500 && bad == null) {
                    bad = resp;
                } else if (status.getStatusCode() >= 500 && bad != null) {
                    HttpEntity entity = resp.getEntity();
                    if (entity != null) {
                        entity.consumeContent();
                    }
                } else {
                    if (bad != null) {
                        HttpEntity entity = bad.getEntity();
                        if (entity != null) {
                            entity.consumeContent();
                        }
                    }
                    return resp;
                }
            }
        }
        if (useBlackList && (blacklisted != null || !blackList.isEmpty()) && !records.isEmpty()) {
            if (blacklisted != null) {
                blackList.keySet().removeAll(blacklisted);
            }
            return resolveRemotePURL(source, qs, accept, language, via, false, bad);
        }
        if (bad != null)
            return bad;
        throw new BadGateway("Couldn't Find Server");
    }

    private HttpResponse resolveRemotePURL(InetSocketAddress addr, String source, String qs, String accept,
            String language, Set<String> via) throws IOException, InterruptedException {
        HTTPObjectClient client = HTTPObjectClient.getInstance();
        String url = qs == null ? source : source + "?" + qs;
        BasicHttpRequest req = new BasicHttpRequest("GET", url);
        if (accept != null) {
            req.setHeader("Accept", accept);
        }
        if (language != null) {
            req.setHeader("Accept-Language", language);
        }
        StringBuilder sb = new StringBuilder();
        for (String v : via) {
            if (v.contains(VIA) && (v.endsWith(VIA) || v.contains(VIA + ",")))
                throw new InternalServerError("Request Loop Detected\n" + via + "\n" + VIA);
            if (sb.length() > 0) {
                sb.append(",");
            }
            sb.append(v);
        }
        sb.append(VIA);
        req.setHeader("Via", sb.toString());
        try {
            HttpResponse resp = client.service(addr, req);
            if (!resp.containsHeader("Via")) {
                String original = "1.1 " + addr.getHostName();
                if (addr.getPort() != 80 && addr.getPort() != 443) {
                    original += ":" + addr.getPort();
                }
                resp.addHeader("Via", original);
            }
            StatusLine status = resp.getStatusLine();
            if (status.getStatusCode() >= 500) {
                ProtocolVersion ver = status.getProtocolVersion();
                String phrase = status.getReasonPhrase();
                resp.setStatusLine(new BasicStatusLine(ver, 502, phrase));
                blackList.put(addr, Boolean.TRUE);
                return resp;
            } else {
                return resp;
            }
        } catch (GatewayTimeout e) {
            blackList.put(addr, Boolean.TRUE);
            return null;
        }
    }

    private List<InetSocketAddress> getBlackListing(boolean useBlackList) {
        if (useBlackList && !blackList.isEmpty())
            return new ArrayList<InetSocketAddress>(blackList.keySet());
        return null;
    }

    private Collection<List<Service>> getAllPURLServices() {
        Map<Number, List<Service>> map = new TreeMap<Number, List<Service>>();
        for (Service srv : getPurlServices()) {
            Number priority = srv.getPurlPriority();
            if (priority == null) {
                priority = 0;
            }
            List<Service> list = map.get(priority);
            if (list == null) {
                map.put(priority, list = new ArrayList<Service>());
            }
            list.add(srv);
        }
        return map.values();
    }

    private InetSocketAddress pickService(List<Service> services) {
        int total = 0;
        List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>();
        for (int i = 0, n = services.size(); i < n; i++) {
            addresses.add(null);
            Service srv = services.get(i);
            Object url = srv.getPurlServer();
            if (url == null)
                continue;
            ParsedURI parsed = new ParsedURI(((RDFObject) url).getResource().stringValue());
            int port = "https".equalsIgnoreCase(parsed.getScheme()) ? 443 : 80;
            InetSocketAddress server = resolve(parsed.getAuthority(), port);
            if (isBlackListed(server) || server.isUnresolved())
                continue;
            addresses.set(i, server);
            Number weight = srv.getPurlWeight();
            total += weight == null ? 1 : weight.intValue();
        }
        total = random(total);
        for (int i = 0, n = services.size(); i < n; i++) {
            Service srv = services.get(i);
            if (addresses.get(i) == null)
                continue;
            Number weight = srv.getPurlWeight();
            total -= weight == null ? 1 : weight.intValue();
            if (total < 0) {
                return addresses.get(i);
            }
        }
        return null;
    }

    private InetSocketAddress resolve(String authority, int port) {
        if (authority.contains("@")) {
            authority = authority.substring(authority.indexOf('@') + 1);
        }
        String hostname = authority;
        if (hostname.contains(":")) {
            hostname = hostname.substring(0, hostname.indexOf(':'));
        }
        if (authority.contains(":")) {
            int idx = authority.indexOf(':') + 1;
            port = Integer.parseInt(authority.substring(idx));
        }
        return new InetSocketAddress(hostname, port);
    }

    private boolean isBlackListed(InetSocketAddress server) {
        return blackList.containsKey(server);
    }

    private int random(int total) {
        if (total <= 0)
            return total;
        return random.get().nextInt(total);
    }

}