nl.b3p.kaartenbalie.service.requesthandler.WFSRequestHandler.java Source code

Java tutorial

Introduction

Here is the source code for nl.b3p.kaartenbalie.service.requesthandler.WFSRequestHandler.java

Source

/*
 * B3P Kaartenbalie is a OGC WMS/WFS proxy that adds functionality
 * for authentication/authorization, pricing and usage reporting.
 *
 * Copyright 2006, 2007, 2008 B3Partners BV
 *
 * This file is part of B3P Kaartenbalie.
 *
 * B3P Kaartenbalie is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * B3P Kaartenbalie is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with B3P Kaartenbalie.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.b3p.kaartenbalie.service.requesthandler;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import nl.b3p.commons.services.B3PCredentials;
import nl.b3p.commons.services.HttpClientConfigured;
import nl.b3p.kaartenbalie.core.server.User;
import nl.b3p.kaartenbalie.core.server.accounting.ExtLayerCalculator;
import nl.b3p.kaartenbalie.core.server.accounting.entity.LayerPriceComposition;
import nl.b3p.kaartenbalie.core.server.accounting.entity.LayerPricing;
import nl.b3p.kaartenbalie.core.server.monitoring.DataMonitoring;
import nl.b3p.kaartenbalie.core.server.monitoring.ServiceProviderRequest;
import nl.b3p.kaartenbalie.core.server.persistence.MyEMFDatabase;
import nl.b3p.kaartenbalie.service.ProviderException;
import nl.b3p.ogc.utils.KBConfiguration;
import nl.b3p.ogc.utils.LayerSummary;
import nl.b3p.ogc.utils.OGCCommunication;
import nl.b3p.ogc.utils.OGCConstants;
import nl.b3p.ogc.utils.OGCRequest;
import nl.b3p.ogc.utils.OGCResponse;
import nl.b3p.ogc.utils.SpLayerSummary;
import nl.b3p.ogc.wfs.v110.WfsLayer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.w3c.dom.Document;

/**
 *
 * @author Chris
 */
public abstract class WFSRequestHandler extends OGCRequestHandler {

    private static final Log log = LogFactory.getLog(WFSRequestHandler.class);

    /**
     * Creates a new instance of WFSRequestHandler
     */
    public WFSRequestHandler() {
    }

    public boolean mayDirectWrite() {
        return false;
    }

    /**
     * @return the maxResponseTime
     */
    public int getMaxResponseTime() {
        if (maxResponseTime <= 0) {
            try {
                maxResponseTime = new Integer(KBConfiguration.WFS_RESPONSE_TIME_LIMIT);
            } catch (NumberFormatException nfe) {
                maxResponseTime = 33333;
            }
        }
        return maxResponseTime;
    }

    public HttpPost createPostMethod(OGCRequest spOgcReq, SpLayerSummary sp, ServiceProviderRequest wfsRequest)
            throws Exception {
        String oldBody = spOgcReq.getXMLBody();
        String body = "";
        // staat bij transactie handler, waarom?
        //        String[] temp = oldBody.split("id");
        //        for (int x = 0; x < temp.length; x++) {
        //            if (x < (temp.length - 1)) {
        //                body += temp[x] + "gml:id";
        //            } else {
        //                body += temp[x];
        //            }
        //        }
        body = oldBody;

        log.debug("WFS POST to serviceprovider: '" + sp.getSpAbbr() + "' with url: '" + sp.getSpUrl()
                + "' and body:");
        log.debug(body);

        // TODO body cleanen
        if (KBConfiguration.SAVE_MESSAGES) {
            wfsRequest.setMessageSent(body);
        }
        wfsRequest.setBytesSent(new Long(body.getBytes().length));

        OGCRequest tmpReq = new OGCRequest(spOgcReq.fixHttpHost(sp.getSpUrl()));
        tmpReq.removeAllWFSParameters();
        String postUrl = spOgcReq.getUrl(tmpReq.getUrl());
        HttpPost method = new HttpPost(postUrl);
        //work around voor ESRI post Messages
        //method.setRequestEntity(new StringRequestEntity(body, "text/xml", "UTF-8"));
        method.setEntity(new StringEntity(body));
        return method;
    }

    public HttpGet createGetMethod(OGCRequest spOgcReq, SpLayerSummary sp, ServiceProviderRequest wfsRequest)
            throws Exception {

        OGCRequest tmpReq = new OGCRequest(spOgcReq.fixHttpHost(sp.getSpUrl()));
        tmpReq.removeAllWFSParameters();
        String getUrl = spOgcReq.getUrl(tmpReq.getUrl());

        log.debug("WFS GET to serviceprovider: '" + sp.getSpAbbr() + "' with url: '" + getUrl.toString() + "'");

        if (KBConfiguration.SAVE_MESSAGES) {
            wfsRequest.setMessageSent(getUrl.toString());
        }
        wfsRequest.setBytesSent(new Long(getUrl.length()));

        return new HttpGet(getUrl.toString());
    }

    protected boolean checkNumberOfSps(List<LayerSummary> lsl, int n) {
        List<String> spl = new ArrayList();
        for (LayerSummary ls : lsl) {
            if (!spl.contains(ls.getSpAbbr())) {
                spl.add(ls.getSpAbbr());
            }
        }
        return spl.size() == n;
    }

    public abstract String prepareRequest4Sp(OGCRequest ogcrequest, SpLayerSummary sp) throws Exception;

    public abstract List<LayerSummary> prepareRequestLayers(OGCRequest ogcrequest) throws Exception;

    public abstract OGCResponse getNewOGCResponse();

    public byte[] prepareDirectWrite(InputStream isx) throws IOException {
        return null;
    }

    public void writeResponse(DataWrapper data, User user) throws Exception {
        OGCResponse ogcresponse = getNewOGCResponse();
        OGCRequest ogcrequest = data.getOgcrequest();

        String version = ogcrequest.getFinalVersion();
        String spInUrl = ogcrequest.getServiceProviderName();

        Integer[] orgIds = user.getOrganizationIds();
        OutputStream os = data.getOutputStream();

        Object identity = null;
        try {
            identity = MyEMFDatabase.createEntityManager(MyEMFDatabase.MAIN_EM);

            boolean forAdmin = isConfigInUrlAndAdmin(data, user);
            // zet layers uit request in een list
            List<LayerSummary> layerSummaryList = prepareRequestLayers(ogcrequest);
            if (layerSummaryList == null) {
                // als geen layers meegegeven, dan alle layers gebruiken
                // alleen bij getcapabilities
                EntityManager em = MyEMFDatabase.getEntityManager(MyEMFDatabase.MAIN_EM);
                String[] al = getOrganisationLayers(em, orgIds, version, forAdmin);
                layerSummaryList = LayerSummary.createLayerSummaryList(Arrays.asList(al), spInUrl, true);
            }

            // maak lijst waarin de layers per sp zijn verzameld
            // boolean om volgorde van de lagen te bewaren
            List<SpLayerSummary> spLayerSummaries = null;
            if (forAdmin) {
                spLayerSummaries = getLayerSummaries(layerSummaryList, spInUrl);
            } else {
                spLayerSummaries = getServiceProviderURLS(layerSummaryList, orgIds, false, data, false);
            }
            if (spLayerSummaries == null || spLayerSummaries.isEmpty()) {
                throw new UnsupportedOperationException(
                        "No Serviceprovider available! User might not have rights to any Serviceprovider!");
            }
            if (spLayerSummaries.size() > 1 && version.equals(OGCConstants.WFS_VERSION_UNSPECIFIED)) {
                // forceren dat alle sp dezelfde versie retourneren, indien meer dan 1
                ogcrequest.addOrReplaceParameter(OGCConstants.VERSION, OGCConstants.WFS_VERSION_110);
            }

            DataMonitoring rr = data.getRequestReporting();
            long startprocestime = System.currentTimeMillis();
            String xmlEncoding = "UTF-8";

            for (SpLayerSummary sp : spLayerSummaries) {
                if (spInUrl != null && !spInUrl.equals(sp.getSpAbbr())) {
                    // sp in url en dit is een andere sp
                    continue;
                }
                sp.setSpInUrl(spInUrl);

                // zet de juiste layers voor deze sp
                OGCRequest sprequest = (OGCRequest) ogcrequest.clone();
                prepareRequest4Sp(sprequest, sp);

                String lurl = sp.getSpUrl();
                if (lurl.length() == 0) {
                    throw new UnsupportedOperationException("No Serviceprovider for this service available!");
                }
                ServiceProviderRequest wfsRequest = this.createServiceProviderRequest(data, lurl,
                        sp.getServiceproviderId(), 0l);

                B3PCredentials credentials = new B3PCredentials();
                credentials.setUserName(sp.getUsername());
                credentials.setPassword(sp.getPassword());
                credentials.setUrl(lurl);
                HttpClientConfigured hcc = new HttpClientConfigured(credentials);

                HttpUriRequest method = null;
                if (sprequest.getHttpMethod().equalsIgnoreCase("POST")) {
                    method = createPostMethod(sprequest, sp, wfsRequest);
                } else { // get
                    method = createGetMethod(sprequest, sp, wfsRequest);
                }

                try {
                    HttpResponse response = hcc.execute(method);

                    try {

                        int statusCode = response.getStatusLine().getStatusCode();
                        wfsRequest.setResponseStatus(statusCode);
                        HttpEntity entity = response.getEntity();

                        if (statusCode != 200) {
                            log.error("Failed to connect with " + method.getURI() + " Using body: "
                                    + sprequest.getXMLBody());
                            throw new UnsupportedOperationException("Failed to connect with " + method.getURI()
                                    + " Using body: " + sprequest.getXMLBody());
                        }

                        wfsRequest.setRequestResponseTime(System.currentTimeMillis() - startprocestime);

                        data.setContentType("text/xml");
                        InputStream is = entity.getContent();

                        InputStream isx = null;
                        byte[] bytes = null;
                        int rsl = 0;
                        try {
                            rsl = new Integer(KBConfiguration.RESPONSE_SIZE_LIMIT);
                        } catch (NumberFormatException nfe) {
                            log.debug("KBConfiguration.RESPONSE_SIZE_LIMIT not properly configured: "
                                    + nfe.getLocalizedMessage());
                        }
                        if (KBConfiguration.SAVE_MESSAGES) {
                            int len = 1;
                            byte[] buffer = new byte[2024];
                            ByteArrayOutputStream bos = new ByteArrayOutputStream();
                            while ((len = is.read(buffer, 0, buffer.length)) > 0) {
                                bos.write(buffer, 0, len);
                                if (buffer.length > rsl && rsl > 0) {
                                    throw new ProviderException(
                                            "Response size exceeds maximum set in configuration:" + buffer.length
                                                    + ", max is: " + rsl);
                                }
                            }
                            bytes = bos.toByteArray();
                            isx = new ByteArrayInputStream(bytes);
                        } else {
                            isx = new CountingInputStream(is);
                        }

                        if (KBConfiguration.SAVE_MESSAGES || spInUrl == null || !mayDirectWrite()) {
                            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                            dbf.setNamespaceAware(true);
                            DocumentBuilder builder = dbf.newDocumentBuilder();
                            Document doc = builder.parse(isx);
                            // indien meerdere sp met verschillende encodings
                            // dan wint de laatste!
                            String docEncoding = doc.getXmlEncoding();
                            if (docEncoding != null) {
                                xmlEncoding = docEncoding;
                            }

                            int len = 0;
                            if (KBConfiguration.SAVE_MESSAGES) {
                                wfsRequest.setMessageReceived(new String(bytes));
                            } else {
                                len = new Integer(((CountingInputStream) isx).getCount());
                                wfsRequest.setBytesReceived(new Long(len));
                            }
                            if (len > rsl && rsl > 0) {
                                throw new ProviderException("Response size exceeds maximum set in configuration:"
                                        + len + ", max is: " + rsl);
                            }

                            String prefix = sp.getSpAbbr();
                            if (spInUrl != null && !spInUrl.isEmpty()) {
                                // sp in url dus geen prefix toevoegen
                                prefix = null;
                            }

                            if (OGCResponse.isWfsV100ErrorResponse(doc.getDocumentElement())) {
                                // wfs 1.0.0 error
                                ogcresponse.rebuildWfsV100ErrorResponse(doc, sprequest, prefix);
                            } else if (OGCResponse.isOwsV100ErrorResponse(doc.getDocumentElement())) {
                                // wfs 1.1.0 error
                                ogcresponse.rebuildOwsV100ErrorResponse(doc, sprequest, prefix);
                            } else {
                                // normale response
                                ogcresponse.rebuildResponse(doc, sprequest, prefix);
                            }
                        } else {
                            /**
                             * Deze methode kan alleen aangeroepen worden als
                             * aan de volgende voorwaarden is voldaan:
                             * <li> slechts n sp nodig voor aanroep
                             * <li> spabbr zit in de url en niet als prefix in
                             * de layer name
                             * <li> KBConfiguration.SAVE_MESSAGES is false Als
                             * aan voorwaarden is voldaan dat wordt direct
                             * doorgestreamd indien er geen fout is opgetreden.
                             * <li> de aanroep methode mayDirectWrite is true.
                             */
                            // direct write possible
                            byte[] h = prepareDirectWrite(isx);
                            if (h != null) {
                                os.write(h);
                            }
                            // write rest
                            IOUtils.copy(isx, os);
                            wfsRequest.setBytesReceived(new Long(((CountingInputStream) isx).getCount()));
                            ogcresponse.setAlreadyDirectWritten(true);
                            break;
                        }
                    } finally {
                        hcc.close(response);
                        hcc.close();
                    }

                } catch (Exception e) {
                    wfsRequest.setExceptionMessage("Failed to send bytes to client: " + e.getMessage());
                    wfsRequest.setExceptionClass(e.getClass());

                    throw e;
                } finally {
                    rr.addServiceProviderRequest(wfsRequest);
                }
            }

            // only write when not already direct written
            if (!ogcresponse.isAlreadyDirectWritten()) {
                String responseBody = ogcresponse.getResponseBody(spLayerSummaries, ogcrequest, xmlEncoding);
                if (responseBody != null && !responseBody.equals("")) {
                    byte[] buffer = responseBody.getBytes(xmlEncoding);
                    os.write(buffer);
                } else {
                    throw new UnsupportedOperationException("XMLbody empty!");
                }
            }
            doAccounting(user.getMainOrganizationId(), data, user);

        } finally {
            log.debug("Closing entity manager .....");
            MyEMFDatabase.closeEntityManager(identity, MyEMFDatabase.MAIN_EM);
        }
    }

    protected LayerPriceComposition calculateLayerPriceComposition(DataWrapper dw, ExtLayerCalculator lc,
            String spAbbr, String layerName) throws Exception {
        String operation = dw.getOperation();
        if (operation == null) {
            log.error("Operation can not be null");
            throw new Exception("Operation can not be null");
        }
        String projection = dw.getOgcrequest().getParameter(OGCConstants.WFS_PARAM_SRSNAME); // todo klopt dit?
        /*
         * De srs parameter word nu alleen gevult met null. Hier moet misschien
         * nog naar gekeken worden, maar nu werk het zo wel.
         */
        BigDecimal scale = (new BigDecimal(dw.getOgcrequest().calcScale())).setScale(2, BigDecimal.ROUND_HALF_UP);
        int planType = LayerPricing.PAY_PER_REQUEST;
        String service = OGCConstants.WFS_SERVICE_WFS;

        return lc.calculateLayerComplete(spAbbr, layerName, new Date(), projection, scale, new BigDecimal("1"),
                planType, service, operation);
    }

    protected SpLayerSummary getValidLayerObjects(EntityManager em, LayerSummary m, Integer[] orgIds,
            boolean b3pLayering) throws Exception {
        String query = "select distinct new " + "nl.b3p.ogc.utils.SpLayerSummary(l, 'true',sp) "
                + "from WfsLayer l, Organization o, WfsServiceProvider sp join o.wfsLayers ol "
                + "where l = ol and " + "l.wfsServiceProvider = sp and " + "o.id in (:orgIds) and "
                + "l.name = :layerName and " + "sp.abbr = :layerCode and " + "sp.allowed = true";

        return getValidLayerObjects(em, query, m, orgIds, b3pLayering);
    }

    protected String[] getOrganisationLayers(EntityManager em, Integer[] orgIds, String version, boolean isAdmin)
            throws Exception {
        List<SpLayerSummary> spLayers = null;
        if (!isAdmin) {
            String query = "select distinct new " + "nl.b3p.ogc.utils.SpLayerSummary(l, 'true',sp) "
                    + "from Organization o " + "join o.wfsLayers l " + "join l.wfsServiceProvider sp "
                    + "where o.id in (:orgIds) " + "and sp.allowed = true";
            if (version != null && !version.equals(OGCConstants.WFS_VERSION_UNSPECIFIED)) {
                query += " and sp.wfsVersion = :version";
            }

            Query q = em.createQuery(query);

            if (orgIds != null) {
                List lijst = Arrays.asList(orgIds);
                q.setParameter("orgIds", lijst);
            } else {
                q.setParameter("orgIds", null);
            }

            if (version != null && !version.equals(OGCConstants.WFS_VERSION_UNSPECIFIED)) {
                q.setParameter("version", version);
            }
            spLayers = q.getResultList();
        } else {
            String query = "select distinct new " + "nl.b3p.ogc.utils.SpLayerSummary(l, 'true',sp) "
                    + "from WfsLayer l " + "join l.wfsServiceProvider sp";
            if (version != null && !version.equals(OGCConstants.WFS_VERSION_UNSPECIFIED)) {
                query += " where sp.wfsVersion = :version";
            }

            Query q = em.createQuery(query);
            if (version != null && !version.equals(OGCConstants.WFS_VERSION_UNSPECIFIED)) {
                q.setParameter("version", version);
            }
            spLayers = q.getResultList();
        }

        List<String> layers = new ArrayList<String>();
        for (SpLayerSummary spls : spLayers) {
            String ln = OGCCommunication.attachSp(spls.getSpAbbr(), spls.getLayerName());
            layers.add(ln);
        }
        return (String[]) layers.toArray(new String[] {});
    }

    /**
     * Create a URI from the ogcrequest with provided URL, by adding the request
     * parameters.
     *
     * @param ogcrequest
     * @param lurl
     * @param version
     * @return
     */
    protected String createUriString(OGCRequest ogcrequest, String lurl, String version) {
        StringBuffer url = new StringBuffer(lurl);
        if (!lurl.endsWith("?")) {
            url.append("?");
        }
        String[] params = ogcrequest.getParametersArray();
        for (int i = 0; i < params.length; i++) {
            String key = params[i].split("=")[0];
            if (key.equalsIgnoreCase(OGCRequest.VERSION)) {
                if (version != null) {// if version not given exclude from params
                    url.append(OGCRequest.VERSION);
                    url.append("=");
                    url.append(version);
                }
            } else {
                url.append(params[i]);
            }
            url.append("&");
        }
        return url.toString();
    }

    /**
     * Create a ServiceProviderRequest for WFS requests.
     *
     * @param data
     * @param url
     * @param spId
     * @param bytesSent
     * @return
     */
    protected ServiceProviderRequest createServiceProviderRequest(DataWrapper data, String url, Integer spId,
            Long bytesSent) {

        DataMonitoring rr = data.getRequestReporting();
        OGCRequest ogcrequest = data.getOgcrequest();

        ServiceProviderRequest wfsRequest = new ServiceProviderRequest();
        wfsRequest.setMsSinceRequestStart(new Long(rr.getMSSinceStart()));
        wfsRequest.setServiceProviderId(spId);
        String version = ogcrequest.getFinalVersion();
        if (version.equals(OGCConstants.WFS_VERSION_UNSPECIFIED)) {
            version = null;
        }
        String uri = createUriString(ogcrequest, url, version);
        wfsRequest.setProviderRequestURI(uri);
        wfsRequest.setWmsVersion(version);
        wfsRequest.setBytesSent(bytesSent);

        return wfsRequest;
    }

    protected List<SpLayerSummary> getLayerSummaries(List<LayerSummary> lsl) throws Exception {
        return getLayerSummaries(LayerSummary.getLayersAsArray(lsl), null);
    }

    protected List<SpLayerSummary> getLayerSummaries(List<LayerSummary> lsl, String serviceName) throws Exception {
        return getLayerSummaries(LayerSummary.getLayersAsArray(lsl), serviceName);
    }

    private List<SpLayerSummary> getLayerSummaries(String[] layers) throws Exception {
        return getLayerSummaries(layers, null);
    }

    private List<SpLayerSummary> getLayerSummaries(String[] layers, String serviceName) throws Exception {
        EntityManager em = MyEMFDatabase.getEntityManager(MyEMFDatabase.MAIN_EM);

        List<SpLayerSummary> eventualSPList = new ArrayList();
        for (int i = 0; i < layers.length; i++) {
            String layer = layers[i];

            boolean splitName = (serviceName == null || serviceName.isEmpty()) ? true : false;
            LayerSummary m = OGCCommunication.splitLayerWithoutNsFix(layer, splitName, serviceName, null);
            String abbr = m.getSpAbbr();
            String name = OGCCommunication.buildLayerNameWithoutSp(m);

            List matchingLayers = em
                    .createQuery("from WfsLayer l where l.name = :name and l.wfsServiceProvider.abbr = :abbr")
                    .setParameter("name", name).setParameter("abbr", abbr).getResultList();

            if (matchingLayers.isEmpty()) {
                /*
                 * XXX "or no rights" ?? No rights are checked...
                 */
                log.error("layer not found: " + layer);
                throw new Exception(KBConfiguration.REQUEST_NORIGHTS_EXCEPTION + ": " + layer);
            }

            if (matchingLayers.size() > 1) {
                log.error("layers with duplicate names, name: " + layer);
                throw new Exception(KBConfiguration.GETMAP_EXCEPTION);
            }

            WfsLayer l = (WfsLayer) matchingLayers.get(0);
            SpLayerSummary layerInfo = new SpLayerSummary(l, "true");

            addToServerProviderList(eventualSPList, layerInfo, false);
        }

        return eventualSPList;
    }

}