@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();
        }
      }
    }
  public void writeTo(final OutputStream out) throws ServiceException, IOException {
    Timer timer = new Timer();

    // single layer? -> request ENCODED_IMAGE
    if (mapContext.getLayerCount() == 1) {
      MapLayer mapLayer = mapContext.getLayers()[0];
      ILayer layer = loader.findLayer(mapLayer);
      try {
        Pipeline pipeline = loader.getOrCreatePipeline(layer, LayerUseCase.ENCODED_IMAGE);

        ProcessorRequest request = prepareProcessorRequest();
        pipeline.process(
            request,
            new ResponseHandler() {
              public void handle(ProcessorResponse pipeResponse) throws Exception {

                HttpServletResponse response = GeoServerWms.response.get();
                if (pipeResponse == EncodedImageResponse.NOT_MODIFIED) {
                  log.info("Response: 304!");
                  response.setStatus(304);
                } else {
                  long lastModified = ((EncodedImageResponse) pipeResponse).getLastModified();
                  // allow caches and browser clients to cache for 1h
                  // response.setHeader( "Cache-Control", "public,max-age=3600" );
                  if (lastModified > 0) {
                    response.setHeader("Cache-Control", "no-cache,must-revalidate");
                    response.setDateHeader("Last-Modified", lastModified);
                  } else {
                    response.setHeader("Cache-Control", "no-cache,must-revalidate");
                    response.setDateHeader("Expires", 0);
                  }

                  byte[] chunk = ((EncodedImageResponse) pipeResponse).getChunk();
                  int len = ((EncodedImageResponse) pipeResponse).getChunkSize();
                  out.write(chunk, 0, len);
                }
              }
            });
        log.debug("    flushing response stream. (" + timer.elapsedTime() + "ms)");
        out.flush();
      } catch (IOException e) {
        throw e;
      } catch (Exception e) {
        throw new IOException(e);
      }
    }

    // multiple layers -> render into one image
    else {
      List<Job> jobs = new ArrayList();
      final Map<MapLayer, Image> images = new HashMap();

      // run jobs for all layers
      for (final MapLayer mapLayer : mapContext.getLayers()) {
        final ILayer layer = loader.findLayer(mapLayer);
        // job
        UIJob job =
            new UIJob(getClass().getSimpleName() + ": " + layer.getLabel()) {
              protected void runWithException(IProgressMonitor monitor) throws Exception {
                try {
                  // XXX this excludes Cache304 (which supports EncodedImageResponse only)
                  Pipeline pipeline = loader.getOrCreatePipeline(layer, LayerUseCase.IMAGE);

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

      // 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 {
        // result image
        BufferedImage result =
            ImageUtils.createImage(mapContext.getMapWidth(), mapContext.getMapHeight(), null, true);
        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);

        for (MapLayer mapLayer : mapContext.getLayers()) {
          Image layerImage = images.get(mapLayer);

          // load image data
          //                  new javax.swing.ImageIcon( image ).getImage();

          ILayer layer = loader.findLayer(mapLayer);
          int rule = AlphaComposite.SRC_OVER;
          float alpha = ((float) layer.getOpacity()) / 100;

          g.setComposite(AlphaComposite.getInstance(rule, alpha));
          g.drawImage(layerImage, 0, 0, null);
        }
        encodeImage(result, out);
      } finally {
        if (g != null) {
          g.dispose();
        }
      }
    }
  }