Java tutorial
/* Copyright (c) 2001 - 2013 OpenPlans - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.importer.rest; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.logging.LogRecord; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.JSONSerializer; import net.sf.json.util.JSONBuilder; import org.apache.commons.io.IOUtils; import org.geoserver.catalog.CoverageStoreInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.StoreInfo; import org.geoserver.catalog.StyleInfo; import org.geoserver.config.util.XStreamPersister; import org.geoserver.config.util.XStreamPersister.Callback; import org.geoserver.config.util.XStreamPersisterFactory; import org.geoserver.rest.PageInfo; import org.geoserver.rest.RestletException; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.geoserver.importer.Database; import org.geoserver.importer.Directory; import org.geoserver.importer.FileData; import org.geoserver.importer.ImportContext; import org.geoserver.importer.ImportData; import org.geoserver.importer.ImportTask; import org.geoserver.importer.Importer; import org.geoserver.importer.SpatialFile; import org.geoserver.importer.Table; import org.geoserver.importer.mosaic.Granule; import org.geoserver.importer.mosaic.Mosaic; import org.geoserver.importer.transform.AttributeRemapTransform; import org.geoserver.importer.transform.AttributesToPointGeometryTransform; import org.geoserver.importer.transform.CreateIndexTransform; import org.geoserver.importer.transform.DateFormatTransform; import org.geoserver.importer.transform.ImportTransform; import org.geoserver.importer.transform.IntegerFieldToDateTransform; import org.geoserver.importer.transform.ReprojectTransform; import org.geoserver.importer.transform.TransformChain; import org.geoserver.importer.transform.VectorTransformChain; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.restlet.data.Status; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; /** * Utility class for reading/writing import/tasks/etc... to/from JSON. * * @author Justin Deoliveira, OpenGeo */ public class ImportJSONWriter { static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); static { DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT")); } Importer importer; PageInfo page; FlushableJSONBuilder json; public ImportJSONWriter(Importer importer, PageInfo page) { this(importer, page, new ByteArrayOutputStream()); } public ImportJSONWriter(Importer importer, PageInfo page, OutputStream out) { this(importer, page, new OutputStreamWriter(out)); } public ImportJSONWriter(Importer importer, PageInfo page, Writer w) { this.importer = importer; this.page = page; this.json = new FlushableJSONBuilder(w); } public void contexts(Iterator<ImportContext> contexts, int expand) throws IOException { json.object().key("imports").array(); while (contexts.hasNext()) { ImportContext context = contexts.next(); context(context, false, expand); } json.endArray().endObject(); json.flush(); } public void context(ImportContext context, boolean top, int expand) throws IOException { if (top) { json.object().key("import"); } json.object(); json.key("id").value(context.getId()); json.key("href").value(page.rootURI(pathTo(context))); json.key("state").value(context.getState()); if (expand > 0) { json.key("archive").value(context.isArchive()); if (context.getTargetWorkspace() != null) { json.key("targetWorkspace").value(toJSON(context.getTargetWorkspace())); } if (context.getTargetStore() != null) { json.key("targetStore"); store(context.getTargetStore(), null, false, expand - 1); //value(toJSON(context.getTargetStore())); } if (context.getData() != null) { json.key("data"); data(context.getData(), context, expand - 1); } tasks(context.getTasks(), false, expand - 1); } json.endObject(); if (top) { json.endObject(); } json.flush(); } public void tasks(List<ImportTask> tasks, boolean top, int expand) throws IOException { if (top) { json.object(); } json.key("tasks").array(); for (ImportTask task : tasks) { task(task, false, expand); } json.endArray(); if (top) { json.endObject(); } json.flush(); } public void task(ImportTask task, boolean top, int expand) throws IOException { long id = task.getId(); String href = page.rootURI(pathTo(task)); if (top) { json.object().key("task"); } json.object(); json.key("id").value(id); json.key("href").value(href); json.key("state").value(task.getState()); if (expand > 0) { if (task.getUpdateMode() != null) { json.key("updateMode").value(task.getUpdateMode().name()); } //data (used to be source) ImportData data = task.getData(); if (data != null) { json.key("data"); data(data, task, expand - 1); } //target StoreInfo store = task.getStore(); if (store != null) { json.key("target"); store(store, task, false, expand - 1); } json.key("progress").value(href + "/progress"); LayerInfo layer = task.getLayer(); if (layer != null) { // @todo don't know why catalog isn't set here, thought this was set during load from BDBImportStore layer.getResource().setCatalog(importer.getCatalog()); json.key("layer"); layer(task, false, expand - 1); } if (task.getError() != null) { json.key("errorMessage").value(concatErrorMessages(task.getError())); } transformChain(task, false, expand - 1); messages(task.getMessages()); } json.endObject(); if (top) { json.endObject(); } json.flush(); } void store(StoreInfo store, ImportTask task, boolean top, int expand) throws IOException { String type = store instanceof DataStoreInfo ? "dataStore" : store instanceof CoverageStoreInfo ? "coverageStore" : "store"; json.object(); if (task != null) { json.key("href").value(page.rootURI(pathTo(task) + "/target")); } if (expand > 0) { JSONObject obj = toJSON(store); json.key(type).value(obj.get(type)); } else { json.key(type).object().key("name").value(store.getName()).key("type").value(store.getType()) .endObject(); } json.endObject(); json.flush(); } void layer(ImportTask task, boolean top, int expand) throws IOException { if (top) { json.object().key("layer"); } LayerInfo layer = task.getLayer(); ResourceInfo r = layer.getResource(); json.object().key("name").value(layer.getName()).key("href").value(page.rootURI(pathTo(task) + "/layer")); if (expand > 0) { json.key("originalName").value(task.getOriginalLayerName()); if (r != null) { json.key("nativeName").value(r.getNativeName()); if (r.getSRS() != null) { json.key("srs").value(r.getSRS()); } if (r.getNativeBoundingBox() != null) { json.key("bbox"); bbox(json, r.getNativeBoundingBox()); } } StyleInfo s = layer.getDefaultStyle(); if (s != null) { style(s, task, false, expand - 1); } } json.endObject(); if (top) { json.endObject(); } json.flush(); } void style(StyleInfo style, ImportTask task, boolean top, int expand) throws IOException { if (top) { json.object(); } String href = page.rootURI(pathTo(task) + "/layer/style"); json.key("style"); if (expand > 0) { JSONObject obj = toJSON(style).getJSONObject("style"); obj.put("href", href); json.value(obj); } else { json.object(); json.key("name").value(style.getName()); json.key("href").value(href); json.endObject(); } if (top) { json.endObject(); } } void transformChain(ImportTask task, boolean top, int expand) throws IOException { if (top) { json.object(); } TransformChain<? extends ImportTransform> txChain = task.getTransform(); json.key("transformChain").object(); json.key("type").value(txChain instanceof VectorTransformChain ? "vector" : "raster"); json.key("transforms").array(); if (txChain != null) { for (int i = 0; i < txChain.getTransforms().size(); i++) { transform(txChain.getTransforms().get(i), i, task, false, expand); } } json.endArray(); json.endObject(); if (top) { json.endObject(); } json.flush(); } public void transform(ImportTransform transform, int index, ImportTask task, boolean top, int expand) throws IOException { json.object(); json.key("type").value(transform.getClass().getSimpleName()); json.key("href").value(page.rootURI(pathTo(task) + "/transform/" + index)); if (expand > 0) { if (transform instanceof DateFormatTransform) { DateFormatTransform df = (DateFormatTransform) transform; json.key("field").value(df.getField()); if (df.getDatePattern() != null) { json.key("format").value(df.getDatePattern().dateFormat().toPattern()); } } else if (transform instanceof IntegerFieldToDateTransform) { IntegerFieldToDateTransform df = (IntegerFieldToDateTransform) transform; json.key("field").value(df.getField()); } else if (transform instanceof CreateIndexTransform) { CreateIndexTransform df = (CreateIndexTransform) transform; json.key("field").value(df.getField()); } else if (transform instanceof AttributeRemapTransform) { AttributeRemapTransform art = (AttributeRemapTransform) transform; json.key("field").value(art.getField()); json.key("target").value(art.getType().getName()); } else if (transform.getClass() == AttributesToPointGeometryTransform.class) { AttributesToPointGeometryTransform atpgt = (AttributesToPointGeometryTransform) transform; json.key("latField").value(atpgt.getLatField()); json.key("lngField").value(atpgt.getLngField()); } else if (transform.getClass() == ReprojectTransform.class) { ReprojectTransform rt = (ReprojectTransform) transform; json.key("source").value(srs(rt.getSource())); json.key("target").value(srs(rt.getTarget())); } else { throw new IOException("Serializaiton of " + transform.getClass() + " not implemented"); } } json.endObject(); json.flush(); } void bbox(JSONBuilder json, ReferencedEnvelope bbox) { json.object().key("minx").value(bbox.getMinX()).key("miny").value(bbox.getMinY()).key("maxx") .value(bbox.getMaxX()).key("maxy").value(bbox.getMaxY()); CoordinateReferenceSystem crs = bbox.getCoordinateReferenceSystem(); if (crs != null) { json.key("crs").value(crs.toWKT()); } json.endObject(); } public void data(ImportData data, Object parent, int expand) throws IOException { if (data instanceof FileData) { if (data instanceof Directory) { if (data instanceof Mosaic) { mosaic((Mosaic) data, parent, expand); } else { directory((Directory) data, parent, expand); } } else { file((FileData) data, parent, expand, false); } } else if (data instanceof Database) { database((Database) data, parent, expand); } else if (data instanceof Table) { table((Table) data, parent, expand); } json.flush(); } public void file(FileData data, Object parent, int expand, boolean href) throws IOException { json.object(); json.key("type").value("file"); json.key("format").value(data.getFormat() != null ? data.getFormat().getName() : null); if (href) { json.key("href").value(page.rootURI(pathTo(data, parent))); } if (expand > 0) { json.key("location").value(data.getFile().getParentFile().getPath()); if (data.getCharsetEncoding() != null) { json.key("charset").value(data.getCharsetEncoding()); } fileContents(data, parent, expand); message(data); } else { json.key("file").value(data.getFile().getName()); } json.endObject(); json.flush(); } void fileContents(FileData data, Object parent, int expand) throws IOException { //TODO: we should probably url encode to handle spaces and other chars String filename = data.getFile().getName(); json.key("file").value(filename); json.key("href").value(page.rootURI(pathTo(data, parent) + "/files/" + filename)); if (expand > 0) { if (data instanceof SpatialFile) { SpatialFile sf = (SpatialFile) data; json.key("prj").value(sf.getPrjFile() != null ? sf.getPrjFile().getName() : null); json.key("other").array(); for (File supp : ((SpatialFile) data).getSuppFiles()) { json.value(supp.getName()); } json.endArray(); if (sf instanceof Granule) { Granule g = (Granule) sf; if (g.getTimestamp() != null) { json.key("timestamp").value(DATE_FORMAT.format(g.getTimestamp())); } } } } } public void mosaic(Mosaic data, Object parent, int expand) throws IOException { directory(data, "mosaic", parent, expand); } public void directory(Directory data, Object parent, int expand) throws IOException { directory(data, "directory", parent, expand); } public void directory(Directory data, String typeName, Object parent, int expand) throws IOException { json.object(); json.key("type").value(typeName); if (data.getFormat() != null) { json.key("format").value(data.getFormat().getName()); } json.key("location").value(data.getFile().getPath()); json.key("href").value(page.rootURI(pathTo(data, parent))); if (expand > 0) { if (data.getCharsetEncoding() != null) { json.key("charset").value(data.getCharsetEncoding()); } json.key("files"); files(data, parent, false, expand - 1); message(data); } json.endObject(); json.flush(); } public void files(Directory data, Object parent, boolean top, int expand) throws IOException { if (top) { json.object().key("files"); } json.array(); for (FileData file : data.getFiles()) { json.object(); fileContents(file, parent, expand - 1); json.endObject(); } json.endArray(); if (top) { json.endObject(); } json.flush(); } public void database(Database data, Object parent, int expand) throws IOException { json.object(); json.key("type").value("database"); json.key("format").value(data.getFormat() != null ? data.getFormat().getName() : null); json.key("href").value(page.rootURI(pathTo(data, parent))); if (expand > 0) { json.key("parameters").object(); for (Map.Entry e : data.getParameters().entrySet()) { json.key((String) e.getKey()).value(e.getValue()); } json.endObject(); json.key("tables").array(); for (Table t : data.getTables()) { json.value(t.getName()); } message(data); json.endArray(); } json.endObject(); } void table(Table data, Object parent, int expand) throws IOException { json.object(); json.key("type").value("table"); json.key("name").value(data.getName()); json.key("format").value(data.getFormat() != null ? data.getFormat().getName() : null); json.key("href").value(page.rootURI(pathTo(data, parent))); json.endObject(); } void message(ImportData data) throws IOException { if (data.getMessage() != null) { json.key("message").value(data.getMessage()); } } void messages(List<LogRecord> records) { if (!records.isEmpty()) { json.key("messages"); json.array(); for (int i = 0; i < records.size(); i++) { LogRecord record = records.get(i); json.object(); json.key("level").value(record.getLevel().toString()); json.key("message").value(record.getMessage()); json.endObject(); } json.endArray(); } } String concatErrorMessages(Throwable ex) { StringBuilder buf = new StringBuilder(); while (ex != null) { if (buf.length() > 0) { buf.append('\n'); } if (ex.getMessage() != null) { buf.append(ex.getMessage()); } ex = ex.getCause(); } return buf.toString(); } FlushableJSONBuilder builder(OutputStream out) { return new FlushableJSONBuilder(new OutputStreamWriter(out)); } JSONObject toJSON(Object o) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); toJSON(o, out); return (JSONObject) JSONSerializer.toJSON(new String(out.toByteArray())); } void toJSON(Object o, OutputStream out) throws IOException { toJSON(o, out, null); } void toJSON(Object o, OutputStream out, Callback callback) throws IOException { XStreamPersister xp = persister(); if (callback != null) { xp.setCallback(callback); } xp.save(o, out); out.flush(); } XStreamPersister persister() { XStreamPersister xp = importer.initXStreamPersister(new XStreamPersisterFactory().createJSONPersister()); xp.setReferenceByName(true); xp.setExcludeIds(); //xp.setCatalog(importer.getCatalog()); xp.setHideFeatureTypeAttributes(); // @todo this is copy-and-paste from org.geoserver.catalog.rest.FeatureTypeResource xp.setCallback(new XStreamPersister.Callback() { @Override protected void postEncodeFeatureType(FeatureTypeInfo ft, HierarchicalStreamWriter writer, MarshallingContext context) { try { writer.startNode("attributes"); context.convertAnother(ft.attributes()); writer.endNode(); } catch (IOException e) { throw new RuntimeException("Could not get native attributes", e); } } }); return xp; } String srs(CoordinateReferenceSystem crs) { return CRS.toSRS(crs); } static String pathTo(ImportContext context) { return "/imports/" + context.getId(); } static String pathTo(ImportTask task) { return pathTo(task.getContext()) + "/tasks/" + task.getId(); } String pathTo(Object parent) { if (parent instanceof ImportContext) { return pathTo((ImportContext) parent); } else if (parent instanceof ImportTask) { return pathTo((ImportTask) parent); } else { throw new IllegalArgumentException("Don't recognize: " + parent); } } String pathTo(ImportData data, Object parent) { return pathTo(parent) + "/data"; } static RestletException badRequest(String error) { JSONObject errorResponse = new JSONObject(); JSONArray errors = new JSONArray(); errors.add(error); errorResponse.put("errors", errors); JSONRepresentation rep = new JSONRepresentation(errorResponse); return new RestletException(rep, Status.CLIENT_ERROR_BAD_REQUEST); } public static class FlushableJSONBuilder extends JSONBuilder { public FlushableJSONBuilder(Writer w) { super(w); } public void flush() throws IOException { writer.flush(); } } }