@Override
    public void sendContent(
        OutputStream out, Range range, Map<String, String> params, String contentType)
        throws IOException, BadRequestException {
      WorkbenchState state = WorkbenchState.instance(SessionContext.current());
      IMap map = state.getMap();

      try {
        JSONObject json = new JSONObject();
        json.put("width", -1);
        json.put("height", -1);

        ReferencedEnvelope extent = map.getExtent();
        json.put("minX", extent.getMinX());
        json.put("minY", extent.getMinY());
        json.put("maxX", extent.getMaxX());
        json.put("maxY", extent.getMaxY());

        out.write(json.toString(4).getBytes("UTF-8"));
      } catch (JSONException e) {
        throw new RuntimeException(e);
      }
    }
    @Override
    public void sendContent(
        OutputStream out, Range range, Map<String, String> params, String contentType)
        throws IOException, BadRequestException {
      WorkbenchState state = WorkbenchState.instance(SessionContext.current());
      try {
        FeatureCollection features = state.getSelectedFeatures();
        if (features != null) {
          FeatureJSON encoder = new FeatureJSON();
          encoder.setEncodeFeatureBounds(false);
          encoder.setEncodeFeatureCollectionBounds(false);
          encoder.setEncodeFeatureCollectionCRS(false);
          encoder.setEncodeFeatureCRS(false);

          encoder.writeFeatureCollection(features, out);
        } else {
          out.write("{\"type\": \"FeatureCollection\",\"features\": []}".getBytes("UTF-8"));
        }
      } catch (Exception e) {
        log.debug("", e);
        throw new RuntimeException(e);
      }
    }
    @Override
    public void sendContent(
        OutputStream out, Range range, final Map<String, String> params, String rContentType)
        throws IOException, BadRequestException {
      final int width = params.containsKey("width") ? Integer.parseInt(params.get("width")) : 300;
      final int height =
          params.containsKey("height") ? Integer.parseInt(params.get("height")) : 300;

      List<Job> jobs = new ArrayList();
      final Map<ILayer, Image> images = new HashMap();

      // run jobs for all layers
      WorkbenchState state = WorkbenchState.instance(SessionContext.current());
      final IMap map = state.getMap();
      if (map != null) {
        for (final ILayer layer : map.getLayers()) {
          if (layer.isVisible()) {
            UIJob job =
                new UIJob(getClass().getSimpleName() + ": " + layer.getLabel()) {
                  protected void runWithException(IProgressMonitor monitor) throws Exception {
                    try {
                      IGeoResource res = layer.getGeoResource();
                      if (res == null) {
                        throw new RuntimeException(
                            "Unable to find geo resource of layer: " + layer);
                      }
                      IService service = res.service(null);
                      Pipeline pipeline =
                          pipelineIncubator.newPipeline(
                              LayerUseCase.IMAGE, layer.getMap(), layer, service);
                      if (pipeline.length() == 0) {
                        throw new RuntimeException(
                            "Unable to build processor pipeline for layer: " + layer);
                      }

                      // processor request
                      GetMapRequest request =
                          new GetMapRequest(
                              null, // layers
                              map.getCRSCode(),
                              map.getExtent(),
                              contentType,
                              width,
                              height,
                              -1);

                      // process request
                      pipeline.process(
                          request,
                          new ResponseHandler() {
                            public void handle(ProcessorResponse pipeResponse) throws Exception {
                              Image image = ((ImageResponse) pipeResponse).getImage();
                              images.put(layer, image);
                            }
                          });
                    } catch (Exception e) {
                      // XXX put a special image in the map
                      log.warn("", e);
                      images.put(layer, null);
                      throw e;
                    }
                  }
                };
            jobs.add(job);
            job.schedule();
          }
        }

        // join jobs
        for (Job job : jobs) {
          try {
            job.join();
          } catch (InterruptedException e) {
            // XXX put a special image in the map
            log.warn("", e);
          }
        }
      }

      // put images together (MapContext order)
      Graphics2D g = null;
      try {
        // create image
        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
        g = result.createGraphics();

        // rendering hints
        RenderingHints hints =
            new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        hints.add(
            new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
        hints.add(
            new RenderingHints(
                RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON));
        g.setRenderingHints(hints);

        if (map == null) {
          g.setFont(new Font("Serif", Font.PLAIN, 14));
          g.setColor(Color.RED);
          g.drawString("Melden Sie sich in der Workbench an, um hier eine Karte zu sehen!", 50, 50);
        }
        // FIXME honor layer.getOrderKey()
        for (Map.Entry<ILayer, Image> entry : images.entrySet()) {
          int rule = AlphaComposite.SRC_OVER;
          float alpha = ((float) entry.getKey().getOpacity()) / 100;

          g.setComposite(AlphaComposite.getInstance(rule, alpha));
          g.drawImage(entry.getValue(), 0, 0, null);
        }

        // encode image
        encodeImage(result, out);
      } finally {
        if (g != null) {
          g.dispose();
        }
      }
    }
 @Override
 public Date getModifiedDate() {
   WorkbenchState state = WorkbenchState.instance(SessionContext.current());
   return state.getMapModified();
 }