IbisReportsActionBean.java
/*
* Copyright (C) 2016 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.stripes;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.activation.MimetypesFileTypeMap;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletResponse;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Before;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StreamingResolution;
import net.sourceforge.stripes.action.StrictBinding;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.validation.DateTypeConverter;
import net.sourceforge.stripes.validation.Validate;
import nl.b3p.viewer.config.app.Application;
import nl.b3p.viewer.config.app.ConfiguredAttribute;
import nl.b3p.viewer.config.security.Authorizations;
import nl.b3p.viewer.config.services.AttributeDescriptor;
import nl.b3p.viewer.config.services.JDBCFeatureSource;
import nl.b3p.viewer.config.services.SimpleFeatureType;
import nl.b3p.viewer.features.CSVDownloader;
import nl.b3p.viewer.features.ExcelDownloader;
import nl.b3p.viewer.features.FeatureDownloader;
import nl.b3p.viewer.features.ShapeDownloader;
import nl.b3p.viewer.ibis.util.IbisConstants;
import nl.b3p.web.stripes.ErrorMessageResolution;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.util.factory.GeoTools;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.stripesstuff.stripersist.Stripersist;
/**
*
* @author mprins
*/
@UrlBinding("/action/ibisreports")
@StrictBinding
public class IbisReportsActionBean implements ActionBean, IbisConstants {
private static final Log log = LogFactory.getLog(IbisReportsActionBean.class);
private static final String JSON_METADATA = "metaData";
private ActionBeanContext context;
@Validate(converter = DateTypeConverter.class)
private Date fromDate;
@Validate(converter = DateTypeConverter.class)
private Date toDate;
@Validate
private String regio;
@Validate
private String gemeente;
/**
* download type.
*/
@Validate
private String type;
@Validate
private String report;
@Validate
private Application application;
@Validate
private JDBCFeatureSource attrSource;
@Validate
private String params;
private boolean unauthorized;
@Before(stages = LifecycleStage.EventHandling)
public void checkAuthorization() {
EntityManager em = Stripersist.getEntityManager();
if (application == null || !Authorizations.isApplicationReadAuthorized(application, context.getRequest(), em)) {
unauthorized = true;
}
}
public Resolution download() throws Exception {
JSONObject json = new JSONObject();
if (unauthorized) {
json.put("success", Boolean.FALSE);
json.put("message", "Not authorized");
return new StreamingResolution("application/json", new StringReader(json.toString(4)));
}
File output = null;
try {
SimpleFeatureType sft = attrSource.getFeatureType(report);
Filter f = this.createFilters(sft);
List<AttributeDescriptor> attrs = sft.getAttributes();
SimpleFeatureSource fs = (SimpleFeatureSource) sft.openGeoToolsFeatureSource();
final Query q = new Query(fs.getName().toString());
q.setFilter(createFilters(sft));
log.debug(q);
// cannot forward to download action bean as that uses layers/appLayers
// while we use an attributesource
Map<String, AttributeDescriptor> featureTypeAttributes = new HashMap();
List<ConfiguredAttribute> attributes = new ArrayList();
ConfiguredAttribute ca;
for (AttributeDescriptor ad : attrs) {
featureTypeAttributes.put(ad.getName(), ad);
ca = new ConfiguredAttribute();
ca.setVisible(true);
ca.setAttributeName(ad.getName());
attributes.add(ca);
}
output = convert(sft, fs, q, attributes, featureTypeAttributes);
json.put("success", true);
} catch (Exception e) {
log.error("Error loading features", e);
json.put("success", false);
String message = "Fout bij ophalen features: " + e.toString();
Throwable cause = e.getCause();
while (cause != null) {
message += "; " + cause.toString();
cause = cause.getCause();
}
json.put("message", message);
}
if (json.getBoolean("success")) {
// geen try-with-resources of close() in finally gebruiken, dan werkt het niet meer.
FileInputStream fis = new FileInputStream(output);
try {
StreamingResolution res = new StreamingResolution(MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(output)) {
@Override
public void stream(HttpServletResponse response) throws Exception {
OutputStream out = response.getOutputStream();
IOUtils.copy(fis, out);
fis.close();
}
};
String name = output.getName();
String extension = name.substring(name.lastIndexOf("."));
SimpleDateFormat today = new SimpleDateFormat("yyyy_MM_dd");
String newName = "download-" + report + "-" + today.format(new Date()) + extension;
res.setFilename(newName);
res.setAttachment(true);
return res;
} finally {
output.delete();
}
} else {
return new ErrorMessageResolution(json.getString("message"));
}
}
@DefaultHandler
public Resolution execute() throws Exception {
JSONObject json = new JSONObject();
json.put("success", Boolean.FALSE);
// initial metadata
JSONObject metadata = new JSONObject()
.put("root", "data").put("totalProperty", "total")
.put("successProperty", "success")
.put("messageProperty", "message")
.put("idProperty", "rownum");
json.put(JSON_METADATA, metadata);
String error = null;
if (attrSource == null) {
error = "Invalid parameters.";
} else if (unauthorized) {
error = "Not authorized.";
} else if (report == null) {
error = "Report type is required.";
}
SimpleFeatureType sft = attrSource.getFeatureType(report);
List<AttributeDescriptor> attrs = sft.getAttributes();
SimpleFeatureSource fs = null;
try {
fs = (SimpleFeatureSource) sft.openGeoToolsFeatureSource();
final Query q = new Query(fs.getName().toString());
q.setFilter(createFilters(sft));
log.debug(q);
SimpleFeatureCollection sfc = fs.getFeatures(q);
featuresToJson(sfc, json, attrs/*attribuutnamen*/);
json.put("message", "OK");
json.put("success", Boolean.TRUE);
} catch (Exception e) {
log.error("Error generating report data.", e);
error = e.getLocalizedMessage();
} finally {
if (fs != null) {
fs.getDataStore().dispose();
}
}
if (error != null) {
json.put("success", Boolean.FALSE);
json.put("message", error);
}
log.debug("returning json:" + json);
return new StreamingResolution("application/json", new StringReader(json.toString()));
}
private Filter createFilters(SimpleFeatureType sft) {
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(GeoTools.getDefaultHints());
List<Filter> filters = new ArrayList<>();
// regio
if (this.regio != null && sft.getAttribute("vvr_naam") != null) {
filters.add(ff.equals(ff.property("vvr_naam"), ff.literal(regio)));
}
// regio
if (this.regio != null && sft.getAttribute("wgr_naam") != null) {
filters.add(ff.equals(ff.property("wgr_naam"), ff.literal(regio)));
}
if (this.gemeente != null && sft.getAttribute("gemeentenaam") != null) {
filters.add(ff.equals(ff.property("gemeentenaam"), ff.literal(gemeente)));
}
if (this.toDate != null && sft.getAttribute("datum") != null) {
filters.add(ff.before(ff.property("datum"), ff.literal(toDate)));
}
if (this.fromDate != null && sft.getAttribute("datum") != null) {
filters.add(ff.after(ff.property("datum"), ff.literal(fromDate)));
}
if (filters.size() > 1) {
return ff.and(filters);
} else if (filters.size() == 1) {
return filters.get(0);
} else {
return Filter.INCLUDE;
}
}
/**
* Convert a SimpleFeatureCollection to JSON with metadata.
*
* @param sfc collections of features
* @param json output/appendend to json structure
* @param featureTypeAttributes flamingo attribute descriptors for the
* features
* @throws JSONException if any
*/
private void featuresToJson(SimpleFeatureCollection sfc, JSONObject json,
List<AttributeDescriptor> featureTypeAttributes) throws JSONException {
// metadata for fData fields
JSONArray fields = new JSONArray();
// columns for grid
JSONArray columns = new JSONArray();
// fData payload
JSONArray datas = new JSONArray();
boolean getMetadataFromFirstFeature = true;
try (SimpleFeatureIterator sfIter = sfc.features()) {
while (sfIter.hasNext()) {
SimpleFeature feature = sfIter.next();
JSONObject fData = new JSONObject();
for (AttributeDescriptor attr : featureTypeAttributes) {
String name = attr.getName();
if (getMetadataFromFirstFeature) {
// only load metadata into json this for first feature
JSONObject field = new JSONObject().put("name", name).put("type", attr.getExtJSType());
if (attr.getType().equals(AttributeDescriptor.TYPE_DATE)) {
field.put("dateFormat", "Y-m");
}
fields.put(field);
columns.put(new JSONObject().put("text", (attr.getAlias() != null ? attr.getAlias() : name)).put("dataIndex", name));
}
fData.put(attr.getName(), feature.getAttribute(attr.getName()));
}
datas.put(fData);
getMetadataFromFirstFeature = false;
}
json.getJSONObject(JSON_METADATA).put("fields", fields);
json.getJSONObject(JSON_METADATA).put("columns", columns);
json.put("data", datas);
json.put("total", datas.length());
}
}
private File convert(SimpleFeatureType ft, FeatureSource fs, Query q, List<ConfiguredAttribute> attributes,
Map<String, AttributeDescriptor> featureTypeAttributes) throws IOException {
Map<String, String> attributeAliases = new HashMap<>();
for (AttributeDescriptor ad : ft.getAttributes()) {
if (ad.getAlias() != null) {
attributeAliases.put(ad.getName(), ad.getAlias());
} else {
attributeAliases.put(ad.getName(), ad.getName());
}
}
SimpleFeatureCollection fc = (SimpleFeatureCollection) fs.getFeatures(q);
File f = null;
// alle kolommen autosizen op inhoud
StringBuilder autosizeAttr = new StringBuilder(",autoSize=");
for (ConfiguredAttribute configuredAttribute : attributes) {
autosizeAttr.append(configuredAttribute.getAttributeName()).append("|");
}
params = params + autosizeAttr;
FeatureDownloader downloader = null;
if (type.equalsIgnoreCase("SHP")) {
downloader = new ShapeDownloader(attributes, (SimpleFeatureSource) fs, featureTypeAttributes, attributeAliases, params);
} else if (type.equalsIgnoreCase("XLS")) {
downloader = new ExcelDownloader(attributes, (SimpleFeatureSource) fs, featureTypeAttributes, attributeAliases, params);
} else if (type.equals("CSV")) {
downloader = new CSVDownloader(attributes, (SimpleFeatureSource) fs, featureTypeAttributes, attributeAliases, params);
} else {
throw new IllegalArgumentException("No suitable type given: " + type);
}
try {
downloader.init();
try (SimpleFeatureIterator it = fc.features()) {
while (it.hasNext()) {
SimpleFeature feature = it.next();
downloader.processFeature(feature);
}
}
f = downloader.write();
} catch (IOException ex) {
log.error("Cannot create outputfile: ", ex);
throw ex;
} finally {
fs.getDataStore().dispose();
}
log.debug("returning file " + f);
return f;
}
//<editor-fold defaultstate="collapsed" desc="getters en setters">
@Override
public ActionBeanContext getContext() {
return context;
}
@Override
public void setContext(ActionBeanContext context) {
this.context = context;
}
public Date getFromDate() {
return fromDate;
}
public void setFromDate(Date fromDate) {
this.fromDate = fromDate;
}
public Date getToDate() {
return toDate;
}
public void setToDate(Date toDate) {
this.toDate = toDate;
}
public String getRegio() {
return regio;
}
public void setRegio(String regio) {
this.regio = regio;
}
public String getGemeente() {
return gemeente;
}
public void setGemeente(String gemeente) {
this.gemeente = gemeente;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getReport() {
return report;
}
public void setReport(String report) {
this.report = report;
}
public Application getApplication() {
return application;
}
public void setApplication(Application application) {
this.application = application;
}
public JDBCFeatureSource getAttrSource() {
return attrSource;
}
public void setAttrSource(JDBCFeatureSource attrSource) {
this.attrSource = attrSource;
}
public String getParams() {
return params;
}
public void setParams(String params) {
this.params = params;
}
//</editor-fold>
}