nl.b3p.viewer.config.services.GeoService.java Source code

Java tutorial

Introduction

Here is the source code for nl.b3p.viewer.config.services.GeoService.java

Source

/*
 * Copyright (C) 2011-2013 B3Partners B.V.
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.b3p.viewer.config.services;

import java.util.*;
import javax.persistence.*;
import nl.b3p.viewer.config.ClobElement;
import nl.b3p.viewer.config.security.Authorizations;
import nl.b3p.viewer.config.security.Authorizations.ReadWrite;
import nl.b3p.viewer.util.SelectedContentCache;
import nl.b3p.web.WaitPageStatus;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.MutableObject;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.stripesstuff.stripersist.Stripersist;

/**
 *
 * @author Matthijs Laan
 */
@Entity
@DiscriminatorColumn(name = "protocol")
public abstract class GeoService {
    public static final String PARAM_ONLINE_CHECK_ONLY = "onlineCheckOnly";

    public static final String DETAIL_OVERRIDDEN_URL = "overridenUrl";
    public static final String DETAIL_ORIGINAL_NAME = "originalName";

    public static final String DETAIL_USE_INTERSECT = "useIntersect";

    @Id
    private Long id;

    @Basic(optional = false)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Category category;

    @Basic(optional = false)
    private String url;

    private String username;
    private String password;

    private boolean monitoringEnabled;

    private boolean monitoringStatusOK = true;

    @OneToOne(cascade = CascadeType.PERSIST)
    private Layer topLayer;

    @ElementCollection
    @Column(name = "keyword")
    private Set<String> keywords = new HashSet<String>();

    @Transient
    private List<Layer> layers;

    @Transient
    private Map<Layer, List<Layer>> childrenByParent = null;

    @Basic(optional = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date authorizationsModified = new Date();

    @ElementCollection
    @JoinTable(joinColumns = @JoinColumn(name = "geoservice"))
    // Element wrapper required because of http://opensource.atlassian.com/projects/hibernate/browse/JPA-11
    private Map<String, ClobElement> details = new HashMap<String, ClobElement>();

    @OneToMany(cascade = CascadeType.PERSIST) // Actually @OneToMany, workaround for HHH-1268
    @JoinTable(inverseJoinColumns = @JoinColumn(name = "style_library"))
    @OrderColumn(name = "list_index")
    private List<StyleLibrary> styleLibraries = new ArrayList();

    //<editor-fold defaultstate="collapsed" desc="getters en setters">
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    public Layer getTopLayer() {
        return topLayer;
    }

    public void setTopLayer(Layer topLayer) {
        this.topLayer = topLayer;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Set<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(Set<String> keywords) {
        this.keywords = keywords;
    }

    public boolean isMonitoringEnabled() {
        return monitoringEnabled;
    }

    public void setMonitoringEnabled(boolean monitoringEnabled) {
        this.monitoringEnabled = monitoringEnabled;
    }

    public Date getAuthorizationsModified() {
        return authorizationsModified;
    }

    public void setAuthorizationsModified(Date authorizationsModified) {
        this.authorizationsModified = authorizationsModified;
    }

    public boolean isMonitoringStatusOK() {
        return monitoringStatusOK;
    }

    public void setMonitoringStatusOK(boolean monitoringStatusOK) {
        this.monitoringStatusOK = monitoringStatusOK;
    }

    public Map<String, ClobElement> getDetails() {
        return details;
    }

    public void setDetails(Map<String, ClobElement> details) {
        this.details = details;
    }

    public List<StyleLibrary> getStyleLibraries() {
        return styleLibraries;
    }

    public void setStyleLibraries(List<StyleLibrary> styleLibraries) {
        this.styleLibraries = styleLibraries;
    }
    //</editor-fold>

    @PreRemove
    public void removeAllLayers() {
        EntityManager em = Stripersist.getEntityManager();
        List<Layer> allLayers = em.createQuery("from Layer where service = :this").setParameter("this", this)
                .getResultList();

        for (Layer l : allLayers) {
            l.getChildren().clear();
            em.remove(l);
        }
    }

    public void initLayerCollectionsForUpdate() {
        EntityManager em = Stripersist.getEntityManager();
        // Use separate query instead of one combined one: may lead to lots of
        // duplicate fields depending on the size of each collection
        em.createQuery("from Layer l left join fetch l.crsList where l.service = :this").setParameter("this", this)
                .getResultList();
        em.createQuery("from Layer l left join fetch l.boundingBoxes where l.service = :this")
                .setParameter("this", this).getResultList();
        em.createQuery("from Layer l left join fetch l.keywords where l.service = :this").setParameter("this", this)
                .getResultList();
        em.createQuery("from Layer l left join fetch l.details where l.service = :this").setParameter("this", this)
                .getResultList();
        em.createQuery("from Layer l left join fetch l.children where l.service = :this").setParameter("this", this)
                .getResultList();
    }

    public GeoService loadFromUrl(String url, Map params) throws Exception {
        return loadFromUrl(url, params, new WaitPageStatus());
    }

    public abstract GeoService loadFromUrl(String url, Map params, WaitPageStatus waitStatus) throws Exception;

    protected static void setAllChildrenDetail(Layer layer) {

        layer.accept(new Layer.Visitor() {

            @Override
            public boolean visit(final Layer l) {

                if (!l.getChildren().isEmpty()) {
                    final MutableObject<List<String>> layerNames = new MutableObject<List<String>>(new ArrayList());
                    l.accept(new Layer.Visitor() {

                        @Override
                        public boolean visit(Layer child) {
                            if (child != l && child.getChildren().isEmpty() && !child.isVirtual()) {
                                layerNames.getValue().add(child.getName());
                            }
                            return true;
                        }
                    });

                    if (!layerNames.getValue().isEmpty()) {
                        l.getDetails().put(Layer.DETAIL_ALL_CHILDREN,
                                new ClobElement(StringUtils.join(layerNames.getValue(), ",")));
                        l.setVirtual(false);
                    }
                }

                return true;
            }
        });
    }

    public void checkOnline() throws Exception {
        Map params = new HashMap();
        params.put(PARAM_ONLINE_CHECK_ONLY, Boolean.TRUE);
        loadFromUrl(getUrl(), params, new WaitPageStatus() {
            @Override
            public void setCurrentAction(String currentAction) {
                // no debug logging
                super.currentAction.set(currentAction);
            }

            @Override
            public void addLog(String message) {
                // no debug logging
                logs.add(message);
            }
        });
    }

    public String getProtocol() {
        return getClass().getAnnotation(DiscriminatorValue.class).value();
    }

    public void authorizationsModified() {
        authorizationsModified = new Date();
    }

    /** To prevent a lot of SQL requests walking a tree structure of entities,
     * load all layers using an efficient query. The Layers.children collections
     * are not initialized, but can be reconstructed from the list of all Layers
     * for this service returned by the query. Call Layer.getLayerChildrenCache() 
     * to retrieve it without causing a SQL query.
     * 
     * The cache is not updated on changes, so will only represent the database
     * state when loadLayerTree() was last called.
     */
    public List<Layer> loadLayerTree() {
        if (layers != null) {
            return layers;
        }

        if (!Stripersist.getEntityManager().contains(this)) {
            // Not a persistent entity (for example when loading user specified 
            // service)
            return Collections.EMPTY_LIST;
        }

        // Retrieve layer tree structure in single query
        layers = Stripersist.getEntityManager().createNamedQuery("getLayerTree")
                .setParameter("rootId", topLayer.getId()).getResultList();

        childrenByParent = new HashMap<Layer, List<Layer>>();
        for (Layer l : layers) {
            if (l.getParent() != null) {
                List<Layer> parentChildren = childrenByParent.get(l.getParent());
                if (parentChildren == null) {
                    parentChildren = new ArrayList<Layer>();
                    childrenByParent.put(l.getParent(), parentChildren);
                }
                parentChildren.add(l);
            }
        }
        return layers;
    }

    public List<Layer> getLayerChildrenCache(Layer l) {
        if (childrenByParent != null) {

            EntityManager em = Stripersist.getEntityManager();

            if (!em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(l.getChildren())) {
                List<Layer> childrenList = childrenByParent.get(l);
                if (childrenList == null) {
                    return Collections.EMPTY_LIST;
                } else {
                    return childrenList;
                }
            } else {
                return l.getChildren();
            }
        } else {
            return l.getChildren();
        }
    }

    public JSONObject toJSONObject(boolean includeLayerTree, Set<String> layersToInclude, boolean validXmlTags)
            throws JSONException {
        return toJSONObject(includeLayerTree, layersToInclude, validXmlTags, false);
    }

    public JSONObject toJSONObject(boolean includeLayerTree, Set<String> layersToInclude, boolean validXmlTags,
            boolean includeAuthorizations) throws JSONException {
        JSONObject o = new JSONObject();
        o.put("id", id);
        o.put("name", name);
        o.put("url", url);
        o.put("protocol", getProtocol());

        if (!validXmlTags) {
            JSONObject jStyleLibraries = new JSONObject();
            for (StyleLibrary sld : getStyleLibraries()) {
                JSONObject jsld = new JSONObject();
                String styleName = sld.getId().toString();
                jStyleLibraries.put("sld:" + styleName, jsld);
                jsld.put("id", sld.getId());
                jsld.put("title", sld.getTitle());
                jsld.put("default", sld.isDefaultStyle());
                if (sld.isDefaultStyle()) {
                    o.put("defaultStyleLibrary", jsld);
                }
                if (sld.getExternalUrl() != null) {
                    jsld.put("externalUrl", sld.getExternalUrl());
                }
                JSONObject userStylesPerNamedLayer = new JSONObject();
                if (sld.getNamedLayerUserStylesJson() != null) {
                    userStylesPerNamedLayer = new JSONObject(sld.getNamedLayerUserStylesJson());
                }
                jsld.put("userStylesPerNamedLayer", userStylesPerNamedLayer);
                if (sld.getExtraLegendParameters() != null) {
                    jsld.put("extraLegendParameters", sld.getExtraLegendParameters());
                }
                jsld.put("hasBody", sld.getExternalUrl() == null);
            }
            o.put("styleLibraries", jStyleLibraries);
        }

        if (topLayer != null) {

            if (Stripersist.getEntityManager().contains(this)) {

                List<Layer> layerEntities = loadLayerTree();

                if (!layerEntities.isEmpty()) {
                    // Prevent n+1 queries
                    Stripersist.getEntityManager()
                            .createQuery("from Layer l " + "left join fetch l.details " + "where l in (:layers)")
                            .setParameter("layers", layerEntities).getResultList();
                }
            }

            JSONObject layers = new JSONObject();
            o.put("layers", layers);
            walkLayerJSONFlatten(topLayer, layers, layersToInclude, validXmlTags, includeAuthorizations);

            if (includeLayerTree) {
                o.put("topLayer", walkLayerJSONTree(topLayer));
            }

        }
        return o;
    }

    private static void walkLayerJSONFlatten(Layer l, JSONObject layers, Set<String> layersToInclude,
            boolean validXmlTags, boolean includeAuthorizations) throws JSONException {

        /* TODO check readers (and include readers in n+1 prevention query */

        /* Flatten tree structure, currently depth-first - later traversed layers
        * do not overwrite earlier layers with the same name - do not include
        * virtual layers
        */

        if (layersToInclude == null || layersToInclude.contains(l.getName())) {
            if (!l.isVirtual() && l.getName() != null && !layers.has(l.getName())) {
                String name = l.getName();
                if (validXmlTags) {
                    /*name="layer_"+name;
                    name=name.replaceAll(" ", "_");*/
                    name = "layer" + layers.length();
                }
                JSONObject layer = l.toJSONObject();
                if (includeAuthorizations) {
                    ReadWrite rw = Authorizations.getLayerAuthorizations(l);
                    layer.put(SelectedContentCache.AUTHORIZATIONS_KEY, rw != null ? rw.toJSON() : new JSONObject());
                }
                layers.put(name, layer);
            }
        }

        for (Layer child : l.getCachedChildren()) {
            walkLayerJSONFlatten(child, layers, layersToInclude, validXmlTags, includeAuthorizations);
        }
    }

    private static JSONObject walkLayerJSONTree(Layer l) throws JSONException {
        JSONObject j = l.toJSONObject();

        List<Layer> children = l.getCachedChildren();
        if (!children.isEmpty()) {
            JSONArray jc = new JSONArray();
            j.put("children", jc);
            for (Layer child : children) {
                jc.put(walkLayerJSONTree(child));
            }
        }
        return j;
    }

    public JSONObject toJSONObject(boolean includeLayerTree) throws JSONException {
        return toJSONObject(includeLayerTree, null, false, false);
    }

    /**
     * Gets a single layer without loading all layers. If multiple layers exist
     * with the same name, a random non-virtual layer is returned.
     */
    public Layer getSingleLayer(final String layerName) {
        try {
            return (Layer) Stripersist.getEntityManager()
                    .createQuery("from Layer where service = :service " + "and name = :n order by virtual desc")
                    .setParameter("service", this).setParameter("n", layerName).setMaxResults(1).getSingleResult();

        } catch (NoResultException nre) {
            return null;
        }
    }

    /**
     * Returns the layer with the given name in this server. The first layer in
     * a depth-first tree traversal with the name is returned. If a child has
     * the same name as its parent, the child is returned.
     * @param layerName the layer name to search for
     * @return the Layer or null if not found
     */
    public Layer getLayer(final String layerName) {
        loadLayerTree();

        if (layerName == null || topLayer == null) {
            return null;
        }

        final MutableObject<Layer> layer = new MutableObject(null);

        topLayer.accept(new Layer.Visitor() {
            @Override
            public boolean visit(Layer l) {
                if (StringUtils.equals(l.getName(), layerName)) {
                    layer.setValue(l);
                    return false;
                }
                return true;
            }
        });

        return layer.getValue();
    }
}