/**
   * Initializes the layer, creating internal structures for calculating grid location and so forth.
   *
   * <p>Subclasses shall implement {@link #initializeInternal(GridSetBroker)} for anything else
   */
  @Override
  public final boolean initialize(GridSetBroker gridSetBroker) {

    if (this.expireCacheList == null) {
      this.expireCacheList = new ArrayList<ExpirationRule>(1);

      if (this.expireCache == null) {
        expireCacheList.add(new ExpirationRule(0, GWCVars.CACHE_NEVER_EXPIRE));
      } else {
        int expireCacheInt = Integer.parseInt(expireCache);
        if (expireCacheInt == GWCVars.CACHE_USE_WMS_BACKEND_VALUE) {
          saveExpirationHeaders = true;
        }
        expireCacheList.add(new ExpirationRule(0, expireCacheInt));
      }
    }

    if (this.expireClientsList == null) {
      this.expireClientsList = new ArrayList<ExpirationRule>(1);

      if (this.expireClients == null) {
        expireClientsList.add(new ExpirationRule(0, 7200));
      } else {
        int expireClientsInt = Integer.parseInt(expireClients);

        if (expireClientsInt == GWCVars.CACHE_USE_WMS_BACKEND_VALUE) {
          saveExpirationHeaders = true;
        } else if (expireClientsInt == GWCVars.CACHE_NEVER_EXPIRE) {
          // One year should do
          expireClientsInt = 3600 * 24 * 365;
        }
        expireClientsList.add(new ExpirationRule(0, expireClientsInt));
      }
    }

    try {
      // mimetypes
      this.formats = new ArrayList<MimeType>();
      if (mimeFormats != null) {
        for (String fmt : mimeFormats) {
          formats.add(MimeType.createFromFormat(fmt));
        }
      }
      if (formats.size() == 0) {
        formats.add(0, MimeType.createFromFormat("image/png"));
        formats.add(1, MimeType.createFromFormat("image/jpeg"));
      }
    } catch (GeoWebCacheException gwce) {
      log.error(gwce.getMessage());
      gwce.printStackTrace();
    }

    if (subSets == null) {
      subSets = new HashMap<String, GridSubset>();
    }

    if (this.gridSubsets != null) {
      Iterator<XMLGridSubset> iter = gridSubsets.iterator();
      while (iter.hasNext()) {
        XMLGridSubset xmlGridSubset = iter.next();
        GridSubset gridSubset = xmlGridSubset.getGridSubSet(gridSetBroker);

        if (gridSubset == null) {
          log.error(
              xmlGridSubset.getGridSetName()
                  + " is not known by the GridSetBroker, skipping for layer "
                  + name);
        } else {
          subSets.put(gridSubset.getName(), gridSubset);
        }
      }

      this.gridSubsets = null;
    }

    // Convert version 1.1.x and 1.0.x grid objects
    if (grids != null && !grids.isEmpty()) {
      Iterator<XMLOldGrid> iter = grids.values().iterator();
      while (iter.hasNext()) {
        GridSubset converted = iter.next().convertToGridSubset(gridSetBroker);
        subSets.put(converted.getSRS().toString(), converted);
      }

      // Null it for the garbage collector
      grids = null;
    }

    if (this.subSets.size() == 0) {
      subSets.put(
          gridSetBroker.WORLD_EPSG4326.getName(),
          GridSubsetFactory.createGridSubSet(gridSetBroker.WORLD_EPSG4326));
      subSets.put(
          gridSetBroker.WORLD_EPSG3857.getName(),
          GridSubsetFactory.createGridSubSet(gridSetBroker.WORLD_EPSG3857));
    }

    return initializeInternal(gridSetBroker);
  }
  GridSubset findBestGridSubset(WMSMapContent map) {
    GetMapRequest req = map.getRequest();
    Map formatOpts = req.getFormatOptions();

    GridSetBroker gridSetBroker = gwc.getGridSetBroker();
    GridSet gridSet = null;

    // first check format options to see if explicitly specified
    if (formatOpts.containsKey("gridset")) {
      gridSet = gridSetBroker.get(formatOpts.get("gridset").toString());
    }

    // next check srs
    if (gridSet == null) {
      gridSet = gridSetBroker.get(req.getSRS().toUpperCase());
    }

    if (gridSet != null) {
      return GridSubsetFactory.createGridSubSet(gridSet);
    }

    CoordinateReferenceSystem crs = map.getCoordinateReferenceSystem();

    // look up epsg code
    Integer epsgCode = null;
    try {
      epsgCode = CRS.lookupEpsgCode(crs, false);
    } catch (Exception e) {
      throw new ServiceException("Unable to determine epsg code for " + crs, e);
    }
    if (epsgCode == null) {
      throw new ServiceException("Unable to determine epsg code for " + crs);
    }

    SRS srs = SRS.getSRS(epsgCode);

    // figure out the appropriate grid sub set
    Set<GridSubset> gridSubsets = new LinkedHashSet<GridSubset>();
    for (MapLayerInfo l : req.getLayers()) {
      TileLayer tl = gwc.getTileLayerByName(l.getName());
      if (tl == null) {
        throw new ServiceException("No tile layer for " + l.getName());
      }

      List<GridSubset> theseGridSubsets = tl.getGridSubsetsForSRS(srs);
      if (gridSubsets.isEmpty()) {
        gridSubsets.addAll(theseGridSubsets);
      } else {
        gridSubsets.retainAll(theseGridSubsets);
      }

      if (gridSubsets.isEmpty()) {
        throw new ServiceException(
            "No suitable " + epsgCode + " grid subset for " + req.getLayers());
      }
    }

    if (gridSubsets.size() > 1) {
      if (LOGGER.isLoggable(Level.WARNING)) {
        StringBuilder msg = new StringBuilder("Found multiple grid subsets: ");
        for (GridSubset gs : gridSubsets) {
          msg.append(gs.getName()).append(", ");
        }
        msg.setLength(msg.length() - 2);
        msg.append(". Choosing first.");
        LOGGER.warning(msg.toString());
      }
    }

    return gridSubsets.iterator().next();
  }